diff options
author | James Wahlin <james.wahlin@10gen.com> | 2016-11-15 14:29:54 -0500 |
---|---|---|
committer | James Wahlin <james.wahlin@10gen.com> | 2016-11-16 11:28:56 -0500 |
commit | ed347a4f92a3388ab0f690502a81132577361623 (patch) | |
tree | 5b0e90662137d9a8fcdf7935aaebc79761558eda | |
parent | 4344dbc672937b4d20d229a0763bfc22faf664f6 (diff) | |
download | mongo-ed347a4f92a3388ab0f690502a81132577361623.tar.gz |
SERVER-27030 Improve error for legacy find/getMore on view
-rw-r--r-- | jstests/views/views_legacy.js | 49 | ||||
-rw-r--r-- | src/mongo/db/query/find.cpp | 23 |
2 files changed, 59 insertions, 13 deletions
diff --git a/jstests/views/views_legacy.js b/jstests/views/views_legacy.js index bd31d5bb74e..af2e7d2a380 100644 --- a/jstests/views/views_legacy.js +++ b/jstests/views/views_legacy.js @@ -1,6 +1,6 @@ /** * Tests that views properly reject queries in legacy read mode, and reject writes performed in - * legacy write mode. + * legacy write mode. Also confirms that legacy killCursors execution is successful. * * TODO(SERVER-25641): If the views test suite is moved under core, we can get rid of this test * after ensuring that it is included in a legacy passthrough suite. @@ -13,28 +13,55 @@ let viewsDB = conn.getDB("views_legacy"); assert.commandWorked(viewsDB.dropDatabase()); assert.commandWorked(viewsDB.createView("view", "collection", [])); + let coll = viewsDB.getCollection("collection"); - // Helper function for performing getLastError. - function assertGetLastErrorFailed() { - let gle = viewsDB.runCommand({getLastError: 1}); - assert.commandWorked(gle); - assert.eq(gle.code, ErrorCodes.CommandNotSupportedOnView, tojson(gle)); + for (let i = 0; i < 10; ++i) { + assert.writeOK(coll.insert({a: i})); } - // A view should reject all write CRUD operations performed in legacy write mode. + conn.forceReadMode("legacy"); conn.forceWriteMode("legacy"); + // + // Legacy getMore is explicitly prohibited on views; you must use the getMore command. + // + let cmdRes = + viewsDB.runCommand({find: "view", filter: {a: {$gt: 0}}, sort: {a: 1}, batchSize: 0}); + assert.commandWorked(cmdRes); + let cursor = new DBCommandCursor(viewsDB.getMongo(), cmdRes, 2); + + let err = assert.throws(function() { + cursor.itcount(); + }, [], "Legacy getMore expected to fail on a view cursor"); + assert.eq(ErrorCodes.CommandNotSupportedOnView, err.code, tojson(err)); + + // + // Legacy killcursors is expected to work on views. + // + cmdRes = viewsDB.runCommand({find: "view", filter: {a: {$gt: 0}}, sort: {a: 1}, batchSize: 0}); + assert.commandWorked(cmdRes); + cursor = new DBCommandCursor(viewsDB.getMongo(), cmdRes, 2); + + // When DBCommandCursor is constructed under legacy readMode, cursor.close() will execute a + // legacy killcursors operation. + cursor.close(); + assert.gleSuccess(viewsDB, "legacy killcursors expected to work on view cursor"); + + // + // A view should reject all write CRUD operations performed in legacy write mode. + // viewsDB.view.insert({x: 1}); - assertGetLastErrorFailed(); + assert.gleErrorCode(viewsDB, ErrorCodes.CommandNotSupportedOnView); viewsDB.view.remove({x: 1}); - assertGetLastErrorFailed(); + assert.gleErrorCode(viewsDB, ErrorCodes.CommandNotSupportedOnView); viewsDB.view.update({x: 1}, {x: 2}); - assertGetLastErrorFailed(); + assert.gleErrorCode(viewsDB, ErrorCodes.CommandNotSupportedOnView); + // // Legacy find is explicitly prohibited on views; you must use the find command. - conn.forceReadMode("legacy"); + // let res = assert.throws(function() { viewsDB.view.find({x: 1}).toArray(); }); diff --git a/src/mongo/db/query/find.cpp b/src/mongo/db/query/find.cpp index 079ae46c61c..0f3b27907d9 100644 --- a/src/mongo/db/query/find.cpp +++ b/src/mongo/db/query/find.cpp @@ -267,7 +267,17 @@ Message getMore(OperationContext* txn, // the data within a collection. cursorManager = CursorManager::getGlobalCursorManager(); } else { - ctx = stdx::make_unique<AutoGetCollectionForRead>(txn, nss); + ctx = stdx::make_unique<AutoGetCollectionOrViewForRead>(txn, nss); + auto viewCtx = static_cast<AutoGetCollectionOrViewForRead*>(ctx.get()); + if (viewCtx->getView()) { + uasserted( + ErrorCodes::CommandNotSupportedOnView, + str::stream() << "Namespace " << nss.ns() + << " is a view. OP_GET_MORE operations are not supported on views. " + << "Only clients which support the getMore command can be used to " + "query views."); + } + Collection* collection = ctx->getCollection(); uassert(17356, "collection dropped between getMore calls", collection); cursorManager = collection->getCursorManager(); @@ -514,9 +524,18 @@ std::string runQuery(OperationContext* txn, LOG(2) << "Running query: " << redact(cq->toStringShort()); // Parse, canonicalize, plan, transcribe, and get a plan executor. - AutoGetCollectionForRead ctx(txn, nss); + AutoGetCollectionOrViewForRead ctx(txn, nss); Collection* collection = ctx.getCollection(); + if (ctx.getView()) { + uasserted(ErrorCodes::CommandNotSupportedOnView, + str::stream() + << "Namespace " + << nss.ns() + << " is a view. Legacy find operations are not supported on views. " + << "Only clients which support the find command can be used to query views."); + } + // We have a parsed query. Time to get the execution plan for it. std::unique_ptr<PlanExecutor> exec = uassertStatusOK( getExecutorFind(txn, collection, nss, std::move(cq), PlanExecutor::YIELD_AUTO)); |