summaryrefslogtreecommitdiff
path: root/jstests/core/views/views_creation.js
blob: 2b5316ad400e9da216c212d802c4d36d262178bc (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
/**
 * Test the creation of views with various options.
 *
 * @tags: [
 *   assumes_unsharded_collection,
 *   # applyOps is not available on mongos.
 *   assumes_against_mongod_not_mongos,
 *   assumes_superuser_permissions,
 *   # applyOps is not retryable.
 *   requires_non_retryable_commands,
 * ]
 */
(function() {
"use strict";

// For arrayEq.
load("jstests/aggregation/extras/utils.js");

const viewsDBName = "views_creation";

let viewsDB = db.getSiblingDB(viewsDBName);
assert.commandWorked(viewsDB.dropDatabase());

let collNames = viewsDB.getCollectionNames();
assert.eq(0, collNames.length, tojson(collNames));

// You cannot create a view that starts with 'system.'.
assert.commandFailedWithCode(viewsDB.runCommand({create: "system.special", viewOn: "collection"}),
                             ErrorCodes.InvalidNamespace,
                             "Created an illegal view named 'system.views'");

// Collections that start with 'system.' that are not special to MongoDB fail with a different
// error code.
assert.commandFailedWithCode(viewsDB.runCommand({create: "system.foo", viewOn: "collection"}),
                             ErrorCodes.InvalidNamespace,
                             "Created an illegal view named 'system.foo'");

// Create a collection for test purposes.
assert.commandWorked(viewsDB.runCommand({create: "collection"}));

let pipe = [{$match: {}}];

// Create a "regular" view on a collection.
assert.commandWorked(viewsDB.runCommand({create: "view", viewOn: "collection", pipeline: pipe}));

collNames = viewsDB.getCollectionNames().filter((function(coll) {
    return !coll.startsWith("system.");
}));
assert.eq(2, collNames.length, tojson(collNames));
let res = viewsDB.runCommand({listCollections: 1, filter: {type: "view"}});
assert.commandWorked(res);

// Ensure that the output of listCollections has all the expected options for a view.
let expectedListCollectionsOutput = [{
    name: "view",
    type: "view",
    options: {viewOn: "collection", pipeline: pipe},
    info: {readOnly: true}
}];
assert(arrayEq(res.cursor.firstBatch, expectedListCollectionsOutput), tojson({
           expectedListCollectionsOutput: expectedListCollectionsOutput,
           got: res.cursor.firstBatch
       }));

// Create a view on a non-existent collection.
assert.commandWorked(
    viewsDB.runCommand({create: "viewOnNonexistent", viewOn: "nonexistent", pipeline: pipe}));

// Create a view but don't specify a pipeline; this should default to something sane.
assert.commandWorked(viewsDB.runCommand({create: "viewWithDefaultPipeline", viewOn: "collection"}));

// Specifying a pipeline but no view namespace must fail.
assert.commandFailed(viewsDB.runCommand({create: "viewNoViewNamespace", pipeline: pipe}));

// Create a view on another view.
assert.commandWorked(viewsDB.runCommand({create: "viewOnView", viewOn: "view", pipeline: pipe}));

// View names are constrained to the same limitations as collection names.
assert.commandFailed(viewsDB.runCommand({create: "", viewOn: "collection", pipeline: pipe}));
assert.commandFailedWithCode(
    viewsDB.runCommand({create: "system.local.new", viewOn: "collection", pipeline: pipe}),
    ErrorCodes.InvalidNamespace);
assert.commandFailedWithCode(
    viewsDB.runCommand({create: "dollar$", viewOn: "collection", pipeline: pipe}),
    ErrorCodes.InvalidNamespace);

// You cannot create a view with a $out stage, by itself or nested inside of a different stage.
const ERROR_CODE_OUT_BANNED_IN_LOOKUP = 51047;
const outStage = {
    $out: "nonExistentCollection"
};
assert.commandFailedWithCode(
    viewsDB.runCommand({create: "viewWithOut", viewOn: "collection", pipeline: [outStage]}),
    ErrorCodes.OptionNotSupportedOnView);
assert.commandFailedWithCode(viewsDB.runCommand({
    create: "viewWithOutInLookup",
    viewOn: "collection",
    pipeline: [{$lookup: {from: "other", pipeline: [outStage], as: "result"}}]
}),
                             ERROR_CODE_OUT_BANNED_IN_LOOKUP);
assert.commandFailedWithCode(viewsDB.runCommand({
    create: "viewWithOutInFacet",
    viewOn: "collection",
    pipeline: [{$facet: {output: [outStage]}}]
}),
                             40600);

// These test that, when an existing view in system.views is invalid because of a $out in the
// pipeline, the database errors on creation of a new view.
assert.commandWorked(viewsDB.adminCommand({
    applyOps: [{
        op: "i",
        ns: viewsDBName + ".system.views",
        o: {
            _id: viewsDBName + ".invalidView",
            viewOn: "collection",
            pipeline: [{$project: {_id: false}}, {$out: "notExistingCollection"}]
        }
    }]
}));
assert.commandFailedWithCode(
    viewsDB.runCommand({create: "viewWithBadViewCatalog", viewOn: "collection", pipeline: []}),
    ErrorCodes.OptionNotSupportedOnView);
assert.commandWorked(viewsDB.adminCommand({
    applyOps: [{op: "d", ns: viewsDBName + ".system.views", o: {_id: viewsDBName + ".invalidView"}}]
}));
}());