diff options
-rw-r--r-- | buildscripts/resmokeconfig/suites/sharding_last_stable_mongos_and_mixed_shards.yml | 2 | ||||
-rw-r--r-- | jstests/sharding/invalid_system_views_sharded_collection.js | 135 | ||||
-rw-r--r-- | src/mongo/db/s/set_shard_version_command.cpp | 12 | ||||
-rw-r--r-- | src/mongo/db/views/durable_view_catalog.cpp | 111 | ||||
-rw-r--r-- | src/mongo/db/views/durable_view_catalog.h | 22 | ||||
-rw-r--r-- | src/mongo/db/views/view_catalog.cpp | 63 | ||||
-rw-r--r-- | src/mongo/db/views/view_catalog.h | 19 | ||||
-rw-r--r-- | src/mongo/db/views/view_catalog_test.cpp | 6 |
8 files changed, 294 insertions, 76 deletions
diff --git a/buildscripts/resmokeconfig/suites/sharding_last_stable_mongos_and_mixed_shards.yml b/buildscripts/resmokeconfig/suites/sharding_last_stable_mongos_and_mixed_shards.yml index aa01978d011..669e5ba40c2 100644 --- a/buildscripts/resmokeconfig/suites/sharding_last_stable_mongos_and_mixed_shards.yml +++ b/buildscripts/resmokeconfig/suites/sharding_last_stable_mongos_and_mixed_shards.yml @@ -137,6 +137,8 @@ selector: # Enable when 4.4 becomes last stable - jstests/sharding/explain_exec_stats_on_shards.js - jstests/sharding/refine_collection_shard_key_basic.js + # Enable if SERVER-41813 is backported or 4.4 becomes last-stable + - jstests/sharding/invalid_system_views_sharded_collection.js # Enable when SERVER-41237 is backported - jstests/sharding/graph_lookup.js # Enable when BACKPORT-4650 is completed diff --git a/jstests/sharding/invalid_system_views_sharded_collection.js b/jstests/sharding/invalid_system_views_sharded_collection.js new file mode 100644 index 00000000000..1248a7aee3a --- /dev/null +++ b/jstests/sharding/invalid_system_views_sharded_collection.js @@ -0,0 +1,135 @@ +/** + * 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.writeOK(unshardedColl.insert({b: "boo"})); + + assert.writeOK(db.system.views.insert(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.writeOK(viewsCollection.insert({y: "baz", a: 5}), makeErrorMessage("insert")); + + assert.writeOK(viewsCollection.update({y: "baz"}, {$set: {y: "qux"}}), + makeErrorMessage("update")); + + assert.writeOK(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")); + assert.commandWorked(db.runCommand({drop: unshardedColl.getName()}), + makeErrorMessage("drop")); + + // Drop the offending view so that the validate hook succeeds. + db.system.views.remove(badViewDefinition); + } + + 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(); +}()); diff --git a/src/mongo/db/s/set_shard_version_command.cpp b/src/mongo/db/s/set_shard_version_command.cpp index 0eb2fbde66c..8db4f26f347 100644 --- a/src/mongo/db/s/set_shard_version_command.cpp +++ b/src/mongo/db/s/set_shard_version_command.cpp @@ -222,15 +222,17 @@ public: repl::ReplicationCoordinator::get(opCtx)->canAcceptWritesForDatabase(opCtx, nss.db())); - // Views do not require a shard version check. + boost::optional<Lock::CollectionLock> collLock; + collLock.emplace(opCtx, nss, MODE_IS); + + // Views do not require a shard version check. We do not care about invalid system views + // for this check, only to validate if a view already exists for this namespace. if (autoDb->getDb() && !autoDb->getDb()->getCollection(opCtx, nss) && - ViewCatalog::get(autoDb->getDb())->lookup(opCtx, nss.ns())) { + ViewCatalog::get(autoDb->getDb()) + ->lookupWithoutValidatingDurableViews(opCtx, nss.ns())) { return true; } - boost::optional<Lock::CollectionLock> collLock; - collLock.emplace(opCtx, nss, MODE_IS); - auto* const css = CollectionShardingState::get(opCtx, nss); const ChunkVersion collectionShardVersion = [&] { auto optMetadata = css->getCurrentMetadataIfKnown(); diff --git a/src/mongo/db/views/durable_view_catalog.cpp b/src/mongo/db/views/durable_view_catalog.cpp index 308e5d14d50..30206b54d91 100644 --- a/src/mongo/db/views/durable_view_catalog.cpp +++ b/src/mongo/db/views/durable_view_catalog.cpp @@ -70,68 +70,85 @@ const std::string& DurableViewCatalogImpl::getName() const { return _db->name(); } -Status DurableViewCatalogImpl::iterate(OperationContext* opCtx, Callback callback) { +void DurableViewCatalogImpl::iterate(OperationContext* opCtx, Callback callback) { + _iterate(opCtx, callback, ViewCatalogLookupBehavior::kValidateDurableViews); +} + +void DurableViewCatalogImpl::iterateIgnoreInvalidEntries(OperationContext* opCtx, + Callback callback) { + _iterate(opCtx, callback, ViewCatalogLookupBehavior::kAllowInvalidDurableViews); +} + +void DurableViewCatalogImpl::_iterate(OperationContext* opCtx, + Callback callback, + ViewCatalogLookupBehavior lookupBehavior) { invariant(opCtx->lockState()->isCollectionLockedForMode(_db->getSystemViewsName(), MODE_IS)); Collection* systemViews = _db->getCollection(opCtx, _db->getSystemViewsName()); - if (!systemViews) - return Status::OK(); + if (!systemViews) { + return; + } auto cursor = systemViews->getCursor(opCtx); while (auto record = cursor->next()) { - RecordData& data = record->data; - - // Check the document is valid BSON, with only the expected fields. - // Use the latest BSON validation version. Existing view definitions are allowed to contain - // decimal data even if decimal is disabled. - fassert(40224, validateBSON(data.data(), data.size(), BSONVersion::kLatest)); - BSONObj viewDef = data.toBson(); - - // Check read definitions for correct structure, and refuse reading past invalid - // definitions. Ignore any further view definitions. - bool valid = true; - for (const BSONElement& e : viewDef) { - std::string name(e.fieldName()); - valid &= name == "_id" || name == "viewOn" || name == "pipeline" || name == "collation"; + BSONObj viewDefinition; + try { + viewDefinition = _validateViewDefinition(opCtx, record->data); + uassertStatusOK(callback(viewDefinition)); + } catch (const ExceptionFor<ErrorCodes::InvalidViewDefinition>& ex) { + if (lookupBehavior == ViewCatalogLookupBehavior::kValidateDurableViews) { + throw ex; + } } + } +} - const auto viewName = viewDef["_id"].str(); - const auto viewNameIsValid = NamespaceString::validCollectionComponent(viewName) && - NamespaceString::validDBName(nsToDatabaseSubstring(viewName)); - valid &= viewNameIsValid; +BSONObj DurableViewCatalogImpl::_validateViewDefinition(OperationContext* opCtx, + const RecordData& recordData) { + // Check the document is valid BSON, with only the expected fields. + // Use the latest BSON validation version. Existing view definitions are allowed to contain + // decimal data even if decimal is disabled. + fassert(40224, validateBSON(recordData.data(), recordData.size(), BSONVersion::kLatest)); + BSONObj viewDefinition = recordData.toBson(); - // Only perform validation via NamespaceString if the collection name has been determined to - // be valid. If not valid then the NamespaceString constructor will uassert. - if (viewNameIsValid) { - NamespaceString viewNss(viewName); - valid &= viewNss.isValid() && viewNss.db() == _db->name(); - } + bool valid = true; - valid &= NamespaceString::validCollectionName(viewDef["viewOn"].str()); + for (const BSONElement& e : viewDefinition) { + std::string name(e.fieldName()); + valid &= name == "_id" || name == "viewOn" || name == "pipeline" || name == "collation"; + } - const bool hasPipeline = viewDef.hasField("pipeline"); - valid &= hasPipeline; - if (hasPipeline) { - valid &= viewDef["pipeline"].type() == mongo::Array; - } + const auto viewName = viewDefinition["_id"].str(); + const auto viewNameIsValid = NamespaceString::validCollectionComponent(viewName) && + NamespaceString::validDBName(nsToDatabaseSubstring(viewName)); + valid &= viewNameIsValid; - valid &= - (!viewDef.hasField("collation") || viewDef["collation"].type() == BSONType::Object); + // Only perform validation via NamespaceString if the collection name has been determined to + // be valid. If not valid then the NamespaceString constructor will uassert. + if (viewNameIsValid) { + NamespaceString viewNss(viewName); + valid &= viewNss.isValid() && viewNss.db() == _db->name(); + } - if (!valid) { - return {ErrorCodes::InvalidViewDefinition, - str::stream() << "found invalid view definition " << viewDef["_id"] - << " while reading '" - << _db->getSystemViewsName() - << "'"}; - } + valid &= NamespaceString::validCollectionName(viewDefinition["viewOn"].str()); - Status callbackStatus = callback(viewDef); - if (!callbackStatus.isOK()) { - return callbackStatus; - } + const bool hasPipeline = viewDefinition.hasField("pipeline"); + valid &= hasPipeline; + if (hasPipeline) { + valid &= viewDefinition["pipeline"].type() == mongo::Array; } - return Status::OK(); + + valid &= (!viewDefinition.hasField("collation") || + viewDefinition["collation"].type() == BSONType::Object); + + uassert(ErrorCodes::InvalidViewDefinition, + str::stream() << "found invalid view definition " << viewDefinition["_id"] + << " while reading '" + << _db->getSystemViewsName() + << "'", + valid); + + return viewDefinition; } void DurableViewCatalogImpl::upsert(OperationContext* opCtx, diff --git a/src/mongo/db/views/durable_view_catalog.h b/src/mongo/db/views/durable_view_catalog.h index 41c2f5837c3..03bacb2f3fb 100644 --- a/src/mongo/db/views/durable_view_catalog.h +++ b/src/mongo/db/views/durable_view_catalog.h @@ -41,6 +41,14 @@ namespace mongo { class BSONObj; class Database; class OperationContext; +class RecordData; + +/** + * ViewCatalogLookupBehavior specifies whether a lookup into the view catalog should attempt to + * validate the durable entries that currently exist within the catalog. This validation should + * rarely be skipped. + */ +enum class ViewCatalogLookupBehavior { kValidateDurableViews, kAllowInvalidDurableViews }; /** * Interface for system.views collection operations associated with view catalog management. @@ -60,7 +68,8 @@ public: static void onExternalChange(OperationContext* opCtx, const NamespaceString& name); using Callback = std::function<Status(const BSONObj& view)>; - virtual Status iterate(OperationContext* opCtx, Callback callback) = 0; + virtual void iterate(OperationContext* opCtx, Callback callback) = 0; + virtual void iterateIgnoreInvalidEntries(OperationContext* opCtx, Callback callback) = 0; virtual void upsert(OperationContext* opCtx, const NamespaceString& name, const BSONObj& view) = 0; @@ -77,12 +86,21 @@ class DurableViewCatalogImpl final : public DurableViewCatalog { public: explicit DurableViewCatalogImpl(Database* db) : _db(db) {} - Status iterate(OperationContext* opCtx, Callback callback); + void iterate(OperationContext* opCtx, Callback callback); + + void iterateIgnoreInvalidEntries(OperationContext* opCtx, Callback callback); + void upsert(OperationContext* opCtx, const NamespaceString& name, const BSONObj& view); void remove(OperationContext* opCtx, const NamespaceString& name); const std::string& getName() const; private: + void _iterate(OperationContext* opCtx, + Callback callback, + ViewCatalogLookupBehavior lookupBehavior); + + BSONObj _validateViewDefinition(OperationContext* opCtx, const RecordData& recordData); + Database* const _db; }; } // namespace mongo diff --git a/src/mongo/db/views/view_catalog.cpp b/src/mongo/db/views/view_catalog.cpp index c212bc2d720..b952731f82c 100644 --- a/src/mongo/db/views/view_catalog.cpp +++ b/src/mongo/db/views/view_catalog.cpp @@ -87,10 +87,12 @@ Status ViewCatalog::reloadIfNeeded(OperationContext* opCtx) { NamespaceString(_durable->getName(), NamespaceString::kSystemDotViewsCollectionName), MODE_IS); stdx::unique_lock<stdx::mutex> lk(_mutex); - return _reloadIfNeeded(lk, opCtx); + return _reloadIfNeeded(lk, opCtx, ViewCatalogLookupBehavior::kValidateDurableViews); } -Status ViewCatalog::_reloadIfNeeded(WithLock lk, OperationContext* opCtx) { +Status ViewCatalog::_reloadIfNeeded(WithLock lk, + OperationContext* opCtx, + ViewCatalogLookupBehavior lookupBehavior) { if (_valid.load()) return Status::OK(); @@ -99,7 +101,7 @@ Status ViewCatalog::_reloadIfNeeded(WithLock lk, OperationContext* opCtx) { // Need to reload, first clear our cache. _viewMap.clear(); - Status status = _durable->iterate(opCtx, [&](const BSONObj& view) -> Status { + auto reloadCallback = [&](const BSONObj& view) -> Status { BSONObj collationSpec = view.hasField("collation") ? view["collation"].Obj() : BSONObj(); auto collator = parseCollator(opCtx, collationSpec); if (!collator.isOK()) { @@ -125,15 +127,25 @@ Status ViewCatalog::_reloadIfNeeded(WithLock lk, OperationContext* opCtx) { pipeline, std::move(collator.getValue())); return Status::OK(); - }); - _valid.store(status.isOK()); + }; - if (!status.isOK()) { + try { + if (lookupBehavior == ViewCatalogLookupBehavior::kValidateDurableViews) { + _durable->iterate(opCtx, reloadCallback); + } else if (lookupBehavior == ViewCatalogLookupBehavior::kAllowInvalidDurableViews) { + _durable->iterateIgnoreInvalidEntries(opCtx, reloadCallback); + } else { + MONGO_UNREACHABLE; + } + } catch (const DBException& ex) { + auto status = ex.toStatus(); LOG(0) << "could not load view catalog for database " << _durable->getName() << ": " << status; + return status; } - return status; + _valid.store(true); + return Status::OK(); } void ViewCatalog::iterate(OperationContext* opCtx, ViewIteratorCallback callback) { @@ -142,7 +154,7 @@ void ViewCatalog::iterate(OperationContext* opCtx, ViewIteratorCallback callback NamespaceString(_durable->getName(), NamespaceString::kSystemDotViewsCollectionName), MODE_IS); stdx::lock_guard<stdx::mutex> lk(_mutex); - uassertStatusOK(_reloadIfNeeded(lk, opCtx)); + uassertStatusOK(_reloadIfNeeded(lk, opCtx, ViewCatalogLookupBehavior::kValidateDurableViews)); for (auto&& view : _viewMap) { callback(*view.second); } @@ -324,7 +336,8 @@ Status ViewCatalog::_validateCollation(WithLock lk, const ViewDefinition& view, const std::vector<NamespaceString>& refs) { for (auto&& potentialViewNss : refs) { - auto otherView = _lookup(lk, opCtx, potentialViewNss.ns()); + auto otherView = _lookup( + lk, opCtx, potentialViewNss.ns(), ViewCatalogLookupBehavior::kValidateDurableViews); if (otherView && !CollatorInterface::collatorsMatch(view.defaultCollator(), otherView->defaultCollator())) { @@ -354,7 +367,8 @@ Status ViewCatalog::createView(OperationContext* opCtx, return Status(ErrorCodes::BadValue, "View must be created on a view or collection in the same database"); - if (_lookup(lk, opCtx, StringData(viewName.ns()))) + if (_lookup( + lk, opCtx, StringData(viewName.ns()), ViewCatalogLookupBehavior::kValidateDurableViews)) return Status(ErrorCodes::NamespaceExists, "Namespace already exists"); if (!NamespaceString::validCollectionName(viewOn.coll())) @@ -386,7 +400,8 @@ Status ViewCatalog::modifyView(OperationContext* opCtx, return Status(ErrorCodes::BadValue, "View must be created on a view or collection in the same database"); - auto viewPtr = _lookup(lk, opCtx, viewName.ns()); + auto viewPtr = + _lookup(lk, opCtx, viewName.ns(), ViewCatalogLookupBehavior::kValidateDurableViews); if (!viewPtr) return Status(ErrorCodes::NamespaceNotFound, str::stream() << "cannot modify missing view " << viewName.ns()); @@ -418,7 +433,8 @@ Status ViewCatalog::dropView(OperationContext* opCtx, const NamespaceString& vie _requireValidCatalog(lk, opCtx); // Save a copy of the view definition in case we need to roll back. - auto viewPtr = _lookup(lk, opCtx, viewName.ns()); + auto viewPtr = + _lookup(lk, opCtx, viewName.ns(), ViewCatalogLookupBehavior::kValidateDurableViews); if (!viewPtr) { return {ErrorCodes::NamespaceNotFound, str::stream() << "cannot drop missing view: " << viewName.ns()}; @@ -443,7 +459,8 @@ Status ViewCatalog::dropView(OperationContext* opCtx, const NamespaceString& vie std::shared_ptr<ViewDefinition> ViewCatalog::_lookup(WithLock lk, OperationContext* opCtx, - StringData ns) { + StringData ns, + ViewCatalogLookupBehavior lookupBehavior) { // We expect the catalog to be valid, so short-circuit other checks for best performance. if (MONGO_unlikely(!_valid.load())) { // If the catalog is invalid, we want to avoid references to virtualized or other invalid @@ -451,7 +468,7 @@ std::shared_ptr<ViewDefinition> ViewCatalog::_lookup(WithLock lk, // invalid view definitions. if (!NamespaceString::validCollectionName(ns)) return nullptr; - Status status = _reloadIfNeeded(lk, opCtx); + Status status = _reloadIfNeeded(lk, opCtx, lookupBehavior); // In case of errors we've already logged a message. Only uassert if there actually is // a user connection, as otherwise we'd crash the server. The catalog will remain invalid, // and any views after the first invalid one are ignored. @@ -472,7 +489,17 @@ std::shared_ptr<ViewDefinition> ViewCatalog::lookup(OperationContext* opCtx, Str NamespaceString(_durable->getName(), NamespaceString::kSystemDotViewsCollectionName), MODE_IS); stdx::lock_guard<stdx::mutex> lk(_mutex); - return _lookup(lk, opCtx, ns); + return _lookup(lk, opCtx, ns, ViewCatalogLookupBehavior::kValidateDurableViews); +} + +std::shared_ptr<ViewDefinition> ViewCatalog::lookupWithoutValidatingDurableViews( + OperationContext* opCtx, StringData ns) { + Lock::CollectionLock systemViewsLock( + opCtx, + NamespaceString(_durable->getName(), NamespaceString::kSystemDotViewsCollectionName), + MODE_IS); + stdx::lock_guard<stdx::mutex> lk(_mutex); + return _lookup(lk, opCtx, ns, ViewCatalogLookupBehavior::kAllowInvalidDurableViews); } StatusWith<ResolvedView> ViewCatalog::resolveView(OperationContext* opCtx, @@ -504,11 +531,13 @@ StatusWith<ResolvedView> ViewCatalog::resolveView(OperationContext* opCtx, for (; depth < ViewGraph::kMaxViewDepth; depth++) { // If the catalog has been invalidated, bail and restart. if (!_valid.load()) { - uassertStatusOK(_reloadIfNeeded(lock, opCtx)); + uassertStatusOK( + _reloadIfNeeded(lock, opCtx, ViewCatalogLookupBehavior::kValidateDurableViews)); break; } - auto view = _lookup(lock, opCtx, resolvedNss->ns()); + auto view = _lookup( + lock, opCtx, resolvedNss->ns(), ViewCatalogLookupBehavior::kValidateDurableViews); if (!view) { // Return error status if pipeline is too large. int pipelineSize = 0; diff --git a/src/mongo/db/views/view_catalog.h b/src/mongo/db/views/view_catalog.h index 05509f5e2fb..8aee3c1b730 100644 --- a/src/mongo/db/views/view_catalog.h +++ b/src/mongo/db/views/view_catalog.h @@ -119,6 +119,13 @@ public: std::shared_ptr<ViewDefinition> lookup(OperationContext* opCtx, StringData nss); /** + * Same functionality as above, except this function skips validating durable views in the view + * catalog. + */ + std::shared_ptr<ViewDefinition> lookupWithoutValidatingDurableViews(OperationContext* opCtx, + StringData nss); + + /** * Resolve the views on 'nss', transforming the pipeline appropriately. This function returns a * fully-resolved view definition containing the backing namespace, the resolved pipeline and * the collation to use for the operation. @@ -172,11 +179,17 @@ private: const ViewDefinition& view, const std::vector<NamespaceString>& refs); - std::shared_ptr<ViewDefinition> _lookup(WithLock, OperationContext* opCtx, StringData ns); - Status _reloadIfNeeded(WithLock, OperationContext* opCtx); + std::shared_ptr<ViewDefinition> _lookup(WithLock, + OperationContext* opCtx, + StringData ns, + ViewCatalogLookupBehavior lookupBehavior); + Status _reloadIfNeeded(WithLock, + OperationContext* opCtx, + ViewCatalogLookupBehavior lookupBehavior); void _requireValidCatalog(WithLock lk, OperationContext* opCtx) { - uassertStatusOK(_reloadIfNeeded(lk, opCtx)); + uassertStatusOK( + _reloadIfNeeded(lk, opCtx, ViewCatalogLookupBehavior::kValidateDurableViews)); invariant(_valid.load()); } diff --git a/src/mongo/db/views/view_catalog_test.cpp b/src/mongo/db/views/view_catalog_test.cpp index 8ad236b37df..7802f9a42c5 100644 --- a/src/mongo/db/views/view_catalog_test.cpp +++ b/src/mongo/db/views/view_catalog_test.cpp @@ -78,9 +78,11 @@ public: static const std::string name; using Callback = std::function<Status(const BSONObj& view)>; - virtual Status iterate(OperationContext* opCtx, Callback callback) { + virtual void iterate(OperationContext* opCtx, Callback callback) { + ++_iterateCount; + } + virtual void iterateIgnoreInvalidEntries(OperationContext* opCtx, Callback callback) { ++_iterateCount; - return Status::OK(); } virtual void upsert(OperationContext* opCtx, const NamespaceString& name, const BSONObj& view) { ++_upsertCount; |