summaryrefslogtreecommitdiff
path: root/jstests/aggregation/aggregation_with_uuids.js
blob: 52bcc99545f62cb626f3f77975e073f27a50157c (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
/**
 * Tests for aggregation requests with the collectionUUID parameter.
 * @tags: [
 *   # Change stream aggregations don't support read concerns other than 'majority'
 *   assumes_read_concern_unchanged,
 * ]
 */
(function() {
"use strict";

load("jstests/libs/fixture_helpers.js");  // For 'isMongos'

const dbName = jsTestName();
const collName = "foo";

const testDB = db.getSiblingDB(dbName);
const testColl = testDB.getCollection(collName);

const validateErrorResponse = function(res, collectionUUID, expectedNamespace, actualNamespace) {
    assert.eq(res.collectionUUID, collectionUUID);
    assert.eq(res.expectedNamespace, expectedNamespace);
    assert.eq(res.actualNamespace, actualNamespace);
};

// On mongos, collectionUUID is only supported for $collStats and $indexStats aggregations.
if (FixtureHelpers.isMongos(db)) {
    assert.commandFailedWithCode(
        testDB.runCommand(
            {aggregate: 1, collectionUUID: UUID(), pipeline: [{$match: {}}], cursor: {}}),
        6256300);
    return;
}

const docs = [{_id: 1}, {_id: 2}];

testColl.drop({writeConcern: {w: "majority"}});
assert.commandWorked(testColl.insert(docs));

// Get the namespace's initial UUID.
let collectionInfos = testDB.getCollectionInfos({name: collName});
let uuid = collectionInfos[0].info.uuid;
assert(uuid, "Expected collection " + collName + " to have a UUID.");

// An aggregation with the UUID should succeed and find the same documents as an aggregation with
// the collection name.
let uuidRes = assert.commandWorked(testDB.runCommand(
    {aggregate: collName, collectionUUID: uuid, pipeline: [{$match: {}}], cursor: {}}));
assert.sameMembers(uuidRes.cursor.firstBatch, docs);

let collNameRes = assert.commandWorked(
    testDB.runCommand({aggregate: collName, pipeline: [{$match: {}}], cursor: {}}));
assert.sameMembers(collNameRes.cursor.firstBatch, uuidRes.cursor.firstBatch);

// getMore should work with cursors created by an aggregation with a uuid.
uuidRes = assert.commandWorked(testDB.runCommand(
    {aggregate: collName, pipeline: [{$match: {}}, {$sort: {_id: 1}}], cursor: {batchSize: 1}}));
assert.eq(1, uuidRes.cursor.firstBatch.length, tojson(uuidRes));
assert.eq(docs[0], uuidRes.cursor.firstBatch[0], tojson(uuidRes));

const getMoreRes =
    assert.commandWorked(testDB.runCommand({getMore: uuidRes.cursor.id, collection: collName}));
assert.eq(1, getMoreRes.cursor.nextBatch.length, tojson(getMoreRes));
assert.eq(docs[1], getMoreRes.cursor.nextBatch[0], tojson(getMoreRes));
assert.eq(0, getMoreRes.cursor.id, tojson(getMoreRes));

// An aggregation with collectionUUID throws NamespaceNotFound if the namespace does not exist, even
// if a collection does exist with the given uuid.
let res = assert.commandFailedWithCode(
    testDB.runCommand(
        {aggregate: "doesNotExist", collectionUUID: uuid, pipeline: [{$match: {}}], cursor: {}}),
    ErrorCodes.CollectionUUIDMismatch);
validateErrorResponse(res, uuid, testDB.getName() + '.doesNotExist', testColl.getFullName());

// Drop the collection.
testColl.drop({writeConcern: {w: "majority"}});

// An aggregation with the initial UUID should fail since the namespace doesn't exist.
res = assert.commandFailedWithCode(
    testDB.runCommand(
        {aggregate: collName, collectionUUID: uuid, pipeline: [{$match: {}}], cursor: {}}),
    ErrorCodes.CollectionUUIDMismatch);
validateErrorResponse(res, uuid, testColl.getFullName(), null);

// Now recreate the collection.
assert.commandWorked(testColl.insert(docs));

// An aggregation with the initial UUID should still fail despite the namespace existing.
res = assert.commandFailedWithCode(
    testDB.runCommand(
        {aggregate: collName, collectionUUID: uuid, pipeline: [{$match: {}}], cursor: {}}),
    ErrorCodes.CollectionUUIDMismatch);
validateErrorResponse(res, uuid, testColl.getFullName(), null);

collNameRes = assert.commandWorked(
    testDB.runCommand({aggregate: collName, pipeline: [{$match: {}}], cursor: {}}));
assert.sameMembers(collNameRes.cursor.firstBatch, docs);

// An aggregation with a collectionUUID should fail with CollectionUUIDMismatch if the namespace is
// a view.
const testView = testDB.getCollection("viewCollection");
testView.drop({writeConcern: {w: "majority"}});
assert.commandWorked(testView.runCommand(
    "create", {viewOn: testColl.getName(), pipeline: [], writeConcern: {w: "majority"}}));

res = assert.commandFailedWithCode(
    testDB.runCommand(
        {aggregate: "viewCollection", collectionUUID: uuid, pipeline: [{$match: {}}], cursor: {}}),
    ErrorCodes.CollectionUUIDMismatch);
validateErrorResponse(res, uuid, testDB.getName() + '.viewCollection', null);

//
// Tests for rejecting invalid collectionUUIDs and cases where collectionUUID is not allowed.
//

// collectionUUID must be a UUID.
assert.commandFailedWithCode(
    testDB.runCommand(
        {aggregate: collName, collectionUUID: "NotAUUID", pipeline: [{$match: {}}], cursor: {}}),
    ErrorCodes.TypeMismatch);

// collectionUUID is not allowed with change streams.
assert.commandFailedWithCode(
    testDB.runCommand(
        {aggregate: collName, collectionUUID: uuid, pipeline: [{$changeStream: {}}], cursor: {}}),
    4928900);

// collectionUUID is not allowed with collectionless aggregations.
assert.commandFailedWithCode(
    testDB.runCommand(
        {aggregate: 1, collectionUUID: uuid, pipeline: [{$listLocalSessions: {}}], cursor: {}}),
    4928901);
})();