diff options
7 files changed, 502 insertions, 7 deletions
diff --git a/jstests/change_streams/change_stream_ban_from_views.js b/jstests/change_streams/change_stream_ban_from_views.js index bba7433c6c6..b2789e72155 100644 --- a/jstests/change_streams/change_stream_ban_from_views.js +++ b/jstests/change_streams/change_stream_ban_from_views.js @@ -22,9 +22,11 @@ // Verify that we cannot create a view using a pipeline which begins with $changeStream. assert.commandFailedWithCode( - db.runCommand({create: csViewName, viewOn: collName, pipeline: csPipe}), 40255); + db.runCommand({create: csViewName, viewOn: collName, pipeline: csPipe}), + ErrorCodes.OptionNotSupportedOnView); // We also cannot update an existing view to use a $changeStream pipeline. assert.commandFailedWithCode( - db.runCommand({collMod: normalViewName, viewOn: collName, pipeline: csPipe}), 40255); + db.runCommand({collMod: normalViewName, viewOn: collName, pipeline: csPipe}), + ErrorCodes.OptionNotSupportedOnView); })(); diff --git a/jstests/multiVersion/view_definition_feature_compatibility_version.js b/jstests/multiVersion/view_definition_feature_compatibility_version.js new file mode 100644 index 00000000000..5f85a28430a --- /dev/null +++ b/jstests/multiVersion/view_definition_feature_compatibility_version.js @@ -0,0 +1,139 @@ +/** + * Test that mongod will not allow creation of a view using 3.6 query features when the feature + * compatibility version is older than 3.6. + * + * We restart mongod during the test and expect it to have the same data after restarting. + * @tags: [requires_persistence] + */ + +(function() { + "use strict"; + + const testName = "view_definition_feature_compatibility_version_multiversion"; + const dbpath = MongoRunner.dataPath + testName; + + /** + * Tests the correct behavior of view creation and modification using 3.6 query features with + * different binary versions and feature compatibility versions. + * + * TODO SERVER-31588: Remove FCV 3.4 validation during the 3.7 development cycle. + */ + function testView(pipeline) { + resetDbpath(dbpath); + + let conn = MongoRunner.runMongod({dbpath: dbpath, binVersion: "latest"}); + assert.neq(null, conn, "mongod was unable to start up"); + let testDB = conn.getDB(testName); + + // Explicitly set feature compatibility version 3.6. + assert.commandWorked(testDB.adminCommand({setFeatureCompatibilityVersion: "3.6"})); + + // Create a view using a 3.6 query feature. + assert.commandWorked(testDB.createView("view1", "coll", pipeline)); + + // Update an existing view to use a 3.6 query feature. + assert(testDB.view1.drop(), "Drop of view failed"); + assert.commandWorked(testDB.createView("view1", "coll", [])); + assert.commandWorked( + testDB.runCommand({collMod: "view1", viewOn: "coll", pipeline: pipeline})); + + // Create an empty view which we will attempt to update to use 3.6 query features under + // feature compatibility mode 3.4. + assert.commandWorked(testDB.createView("view2", "coll", [])); + + // Set the feature compatibility version to 3.4. + assert.commandWorked(testDB.adminCommand({setFeatureCompatibilityVersion: "3.4"})); + + // Read against an existing view using 3.6 query features should not fail. + assert.commandWorked(testDB.runCommand({find: "view1"})); + + // Trying to create a new view using 3.6 query features should fail. + assert.commandFailedWithCode(testDB.createView("view_fail", "coll", pipeline), + ErrorCodes.QueryFeatureNotAllowed); + + // Trying to update existing view to use 3.6 query features should also fail. + assert.commandFailedWithCode( + testDB.runCommand({collMod: "view2", viewOn: "coll", pipeline: pipeline}), + ErrorCodes.QueryFeatureNotAllowed); + + MongoRunner.stopMongod(conn); + + // Starting up a 3.4 mongod with 3.6 query features will succeed. + conn = MongoRunner.runMongod({dbpath: dbpath, binVersion: "3.4", noCleanData: true}); + assert.neq(null, conn, "mongod 3.4 was unable to start up"); + testDB = conn.getDB(testName); + + // Reads will fail against views with 3.6 query features when running a 3.4 binary. + // Not checking the code returned on failure as it is not uniform across the various + // 'pipeline' arguments tested. + assert.commandFailed(testDB.runCommand({find: "view1"})); + + // A read against a view that does not contain 3.6 query features succeeds. + assert.commandWorked(testDB.runCommand({find: "view2"})); + + MongoRunner.stopMongod(conn); + + // Starting up a 3.6 mongod should succeed, even though the feature compatibility version is + // still set to 3.4. + conn = MongoRunner.runMongod({dbpath: dbpath, binVersion: "latest", noCleanData: true}); + assert.neq(null, conn, "mongod was unable to start up"); + testDB = conn.getDB(testName); + + // A read against a view with 3.6 query features should now succeed. + assert.commandWorked(testDB.runCommand({find: "view1"})); + + // Set the feature compatibility version back to 3.6. + assert.commandWorked(testDB.adminCommand({setFeatureCompatibilityVersion: "3.6"})); + + // Now we should be able to create a view using 3.6 query features again. + assert.commandWorked(testDB.createView("view3", "coll", pipeline)); + + // And we should be able to modify a view to use 3.6 query features. + assert(testDB.view3.drop(), "Drop of view failed"); + assert.commandWorked(testDB.createView("view3", "coll", [])); + assert.commandWorked( + testDB.runCommand({collMod: "view3", viewOn: "coll", pipeline: pipeline})); + + // Set the feature compatibility version to 3.4 and then restart with + // internalValidateFeaturesAsMaster=false. + assert.commandWorked(testDB.adminCommand({setFeatureCompatibilityVersion: "3.4"})); + MongoRunner.stopMongod(conn); + conn = MongoRunner.runMongod({ + dbpath: dbpath, + binVersion: "latest", + noCleanData: true, + setParameter: "internalValidateFeaturesAsMaster=false" + }); + assert.neq(null, conn, "mongod was unable to start up"); + testDB = conn.getDB(testName); + + // Even though the feature compatibility version is 3.4, we should still be able to create a + // view using 3.6 query features, because internalValidateFeaturesAsMaster is false. + assert.commandWorked(testDB.createView("view4", "coll", pipeline)); + + // We should also be able to modify a view to use 3.6 query features. + assert(testDB.view4.drop(), "Drop of view failed"); + assert.commandWorked(testDB.createView("view4", "coll", [])); + assert.commandWorked( + testDB.runCommand({collMod: "view4", viewOn: "coll", pipeline: pipeline})); + + MongoRunner.stopMongod(conn); + + // Starting up a 3.4 mongod with 3.6 query features. + conn = MongoRunner.runMongod({dbpath: dbpath, binVersion: "3.4", noCleanData: true}); + assert.neq(null, conn, "mongod 3.4 was unable to start up"); + testDB = conn.getDB(testName); + + // Existing views with 3.6 query features can be dropped. + assert(testDB.view1.drop(), "Drop of view failed"); + assert(testDB.system.views.drop(), "Drop of system.views collection failed"); + + MongoRunner.stopMongod(conn); + } + + testView([{$match: {$expr: {$eq: ["$x", "$y"]}}}]); + testView([{$match: {$jsonSchema: {properties: {foo: {type: "string"}}}}}]); + testView([{$facet: {field: [{$match: {$jsonSchema: {properties: {foo: {type: "string"}}}}}]}}]); + testView([{$facet: {field: [{$match: {$expr: {$eq: ["$x", "$y"]}}}]}}]); + testView([{$lookup: {from: "foreign", as: "as", pipeline: []}}]); +}()); diff --git a/jstests/noPassthrough/view_definition_feature_compatibility_version.js b/jstests/noPassthrough/view_definition_feature_compatibility_version.js new file mode 100644 index 00000000000..50ce10f0688 --- /dev/null +++ b/jstests/noPassthrough/view_definition_feature_compatibility_version.js @@ -0,0 +1,72 @@ +// Test that query and aggregation syntax introduced in 3.6 is restricted from view definitions when +// the featureCompatibilityVersion is 3.4. +// TODO SERVER-31588: Remove FCV 3.4 validation during the 3.7 development cycle. + +(function() { + "use strict"; + const conn = MongoRunner.runMongod({}); + assert.neq(null, conn, "mongod was unable to start up"); + + const testDB = conn.getDB("view_definition_feature_compatibility_version"); + assert.commandWorked(testDB.dropDatabase()); + + const adminDB = conn.getDB("admin"); + + function pipelineValidForViewUnderFCV_36(pipeline) { + // Set featureCompatibilityVersion to 3.6. + assert.commandWorked(adminDB.runCommand({setFeatureCompatibilityVersion: "3.6"})); + + // Confirm view creation with 'pipeline' succeeds. + assert.commandWorked(testDB.createView("view", "test", pipeline)); + assert(testDB.view.drop()); + + // Confirm view collMod with 'pipeline' succeeds. + assert.commandWorked(testDB.createView("view", "test", [])); + assert.commandWorked( + testDB.runCommand({collMod: "view", viewOn: "test", pipeline: pipeline})); + assert(testDB.view.drop()); + } + + function pipelineNotValidForViewUnderFCV_34(pipeline) { + // Set featureCompatibilityVersion to 3.4. + assert.commandWorked(adminDB.runCommand({setFeatureCompatibilityVersion: "3.4"})); + + // Confirm view creation with 'pipeline' fails. + assert.commandFailedWithCode(testDB.createView("view", "test", pipeline), + ErrorCodes.QueryFeatureNotAllowed); + + // Confirm view collMod with 'pipeline' fails. + assert.commandWorked(testDB.createView("view", "test", [])); + assert.commandFailedWithCode( + testDB.runCommand({collMod: "view", viewOn: "test", pipeline: pipeline}), + ErrorCodes.QueryFeatureNotAllowed); + assert(testDB.view.drop()); + } + + // $lookup with 'pipeline' syntax. + let pipeline = [{$lookup: {from: "test", as: "as", pipeline: []}}]; + pipelineValidForViewUnderFCV_36(pipeline); + pipelineNotValidForViewUnderFCV_34(pipeline); + + // $match with $jsonSchema. + pipeline = [{$match: {$jsonSchema: {"required": ["x"]}}}]; + pipelineValidForViewUnderFCV_36(pipeline); + pipelineNotValidForViewUnderFCV_34(pipeline); + + // $match with $expr. + pipeline = [{$match: {$expr: {$eq: ["$x", "$y"]}}}]; + pipelineValidForViewUnderFCV_36(pipeline); + pipelineNotValidForViewUnderFCV_34(pipeline); + + // $facet with a subpipeline containing a $match with $jsonSchema. + pipeline = [{$facet: {output: [{$match: {$jsonSchema: {"required": ["x"]}}}]}}]; + pipelineValidForViewUnderFCV_36(pipeline); + pipelineNotValidForViewUnderFCV_34(pipeline); + + // $facet with a subpipeline containing a $match with $expr. + pipeline = [{$facet: {output: [{$match: {$expr: {$eq: ["$x", "$y"]}}}]}}]; + pipelineValidForViewUnderFCV_36(pipeline); + pipelineNotValidForViewUnderFCV_34(pipeline); + + MongoRunner.stopMongod(conn); +}()); diff --git a/jstests/replsets/view_definition_initial_sync_with_feature_compatibility.js b/jstests/replsets/view_definition_initial_sync_with_feature_compatibility.js new file mode 100644 index 00000000000..16b9091b34e --- /dev/null +++ b/jstests/replsets/view_definition_initial_sync_with_feature_compatibility.js @@ -0,0 +1,89 @@ +/** + * Test that a new replica set member can successfully sync a collection with a view using 3.6 query + * features, even when the replica set was downgraded to feature compatibility version 3.4. + * + * We restart the secondary as part of this test with the expectation that it still has the same + * data after the restart. + * @tags: [requires_persistence] + * + * TODO SERVER-31588: Remove FCV 3.4 validation during the 3.7 development cycle. + */ + +load("jstests/replsets/rslib.js"); + +(function() { + "use strict"; + const testName = "view_definition_initial_sync_with_feature_compatibility"; + + function testView(pipeline) { + // + // Create a single-node replica set. + // + let replTest = new ReplSetTest({name: testName, nodes: 1}); + + replTest.startSet(); + replTest.initiate(); + + let primary = replTest.getPrimary(); + let testDB = primary.getDB("test"); + + // + // Explicitly set the replica set to feature compatibility version 3.6. + // + assert.commandWorked(primary.adminCommand({setFeatureCompatibilityVersion: "3.6"})); + + // + // Create a view using 3.6 query features. + // + assert.commandWorked(testDB.createView("view1", "coll", pipeline)); + + // + // Downgrade the replica set to feature compatibility version 3.4. + // + assert.commandWorked(primary.adminCommand({setFeatureCompatibilityVersion: "3.4"})); + + // + // Add a new member to the replica set. + // + let secondaryDBPath = MongoRunner.dataPath + testName + "_secondary"; + resetDbpath(secondaryDBPath); + let secondary = replTest.add({dbpath: secondaryDBPath}); + replTest.reInitiate(secondary); + reconnect(primary); + reconnect(secondary); + + // + // Once the new member completes its initial sync, stop it, remove it from the replica + // set, and start it back up as an individual instance. + // + replTest.waitForState(secondary, [ReplSetTest.State.PRIMARY, ReplSetTest.State.SECONDARY]); + + replTest.stopSet(undefined /* send default signal */, + true /* don't clear data directory */); + + secondary = MongoRunner.runMongod({dbpath: secondaryDBPath, noCleanData: true}); + assert.neq(null, secondary, "mongod was unable to start up"); + + // + // Verify that the view synced to the new member. + // + let secondaryDB = secondary.getDB("test"); + assert.eq(secondaryDB.system.views.findOne({_id: "test.view1"}, {_id: 1}), + {_id: "test.view1"}); + + // + // Verify that, even though a view using 3.6 query features exists, it is not possible to + // create a new view using 3.6 query features because of feature compatibility version 3.4. + // + assert.commandFailedWithCode(secondaryDB.createView("view2", "coll", pipeline), + ErrorCodes.QueryFeatureNotAllowed); + + MongoRunner.stopMongod(secondary); + } + + testView([{$match: {$expr: {$eq: ["$x", "$y"]}}}]); + testView([{$match: {$jsonSchema: {properties: {foo: {type: "string"}}}}}]); + testView([{$facet: {field: [{$match: {$jsonSchema: {properties: {foo: {type: "string"}}}}}]}}]); + testView([{$facet: {field: [{$match: {$expr: {$eq: ["$x", "$y"]}}}]}}]); + testView([{$lookup: {from: "foreign", as: "as", pipeline: []}}]); +}());
\ No newline at end of file diff --git a/src/mongo/db/pipeline/document_source_facet.h b/src/mongo/db/pipeline/document_source_facet.h index 85eb2f0eb69..ff7c12f4ae1 100644 --- a/src/mongo/db/pipeline/document_source_facet.h +++ b/src/mongo/db/pipeline/document_source_facet.h @@ -130,6 +130,10 @@ public: return {this}; } + const std::vector<FacetPipeline>& getFacetPipelines() const { + return _facets; + } + // The following are overridden just to forward calls to sub-pipelines. void addInvolvedCollections(std::vector<NamespaceString>* collections) const final; void doInjectMongoProcessInterface(std::shared_ptr<MongoProcessInterface>) final; diff --git a/src/mongo/db/views/view_catalog.cpp b/src/mongo/db/views/view_catalog.cpp index 67410c7a045..ec654514a1b 100644 --- a/src/mongo/db/views/view_catalog.cpp +++ b/src/mongo/db/views/view_catalog.cpp @@ -38,10 +38,14 @@ #include "mongo/base/status_with.h" #include "mongo/base/string_data.h" #include "mongo/bson/util/builder.h" +#include "mongo/db/commands/feature_compatibility_version_command_parser.h" #include "mongo/db/namespace_string.h" #include "mongo/db/operation_context.h" #include "mongo/db/pipeline/aggregation_request.h" #include "mongo/db/pipeline/document_source.h" +#include "mongo/db/pipeline/document_source_facet.h" +#include "mongo/db/pipeline/document_source_lookup.h" +#include "mongo/db/pipeline/document_source_match.h" #include "mongo/db/pipeline/expression_context.h" #include "mongo/db/pipeline/lite_parsed_pipeline.h" #include "mongo/db/pipeline/pipeline.h" @@ -64,6 +68,62 @@ StatusWith<std::unique_ptr<CollatorInterface>> parseCollator(OperationContext* o } return CollatorFactoryInterface::get(opCtx->getServiceContext())->makeFromBSON(collationSpec); } + +// TODO SERVER-31588: Remove FCV 3.4 validation during the 3.7 development cycle. +Status validInViewUnder34FeatureCompatibility(const boost::intrusive_ptr<ExpressionContext>& expCtx, + const Pipeline& pipeline) { + const auto& sourceList = pipeline.getSources(); + // Confirm that the view pipeline does not contain elements that require 3.6 feature + // compatibility. + for (auto&& source : sourceList) { + if (auto matchStage = dynamic_cast<DocumentSourceMatch*>(source.get())) { + auto query = matchStage->getQuery(); + MatchExpressionParser::AllowedFeatureSet allowedFeatures = + Pipeline::kAllowedMatcherFeatures & + ~MatchExpressionParser::AllowedFeatures::kJSONSchema & + ~MatchExpressionParser::AllowedFeatures::kExpr; + + auto statusWithMatcher = MatchExpressionParser::parse( + query, expCtx, ExtensionsCallbackNoop(), allowedFeatures); + + if (!statusWithMatcher.isOK()) { + if (statusWithMatcher.getStatus().code() == ErrorCodes::QueryFeatureNotAllowed) { + return {statusWithMatcher.getStatus().code(), + str::stream() + << "featureCompatibility version '3.6' is required to create " + "a view containing new features. See " + << feature_compatibility_version::kDochubLink + << "; " + << statusWithMatcher.getStatus().reason()}; + } + + uasserted(ErrorCodes::InternalError, + str::stream() + << "Unexpected error on validation for 3.4 feature compatibility: " + << statusWithMatcher.getStatus().toString()); + } + } else if (auto lookupStage = dynamic_cast<DocumentSourceLookUp*>(source.get())) { + if (lookupStage->wasConstructedWithPipelineSyntax()) { + return {ErrorCodes::QueryFeatureNotAllowed, + str::stream() << "featureCompatibility version '3.6' is required to create " + "a view containing " + "a $lookup stage with 'pipeline' syntax. See " + << feature_compatibility_version::kDochubLink}; + } + } else if (auto facetStage = dynamic_cast<DocumentSourceFacet*>(source.get())) { + for (auto&& facetSubPipe : facetStage->getFacetPipelines()) { + auto status = + validInViewUnder34FeatureCompatibility(expCtx, *facetSubPipe.pipeline); + if (!status.isOK()) { + return status; + } + } + } + } + + return Status::OK(); +} + } // namespace Status ViewCatalog::reloadIfNeeded(OperationContext* opCtx) { @@ -172,7 +232,7 @@ Status ViewCatalog::_upsertIntoGraph(OperationContext* opCtx, const ViewDefiniti // will also return the set of involved namespaces. auto pipelineStatus = _validatePipeline_inlock(opCtx, viewDef); if (!pipelineStatus.isOK()) { - uassert(40255, + uassert(pipelineStatus.getStatus().code(), str::stream() << "Invalid pipeline for view " << viewDef.name().ns() << "; " << pipelineStatus.getStatus().reason(), !needsValidation); @@ -224,7 +284,6 @@ Status ViewCatalog::_upsertIntoGraph(OperationContext* opCtx, const ViewDefiniti StatusWith<stdx::unordered_set<NamespaceString>> ViewCatalog::_validatePipeline_inlock( OperationContext* opCtx, const ViewDefinition& viewDef) const { - // Make a LiteParsedPipeline to determine the namespaces referenced by this pipeline. AggregationRequest request(viewDef.viewOn(), viewDef.pipeline()); const LiteParsedPipeline liteParsedPipeline(request); const auto involvedNamespaces = liteParsedPipeline.getInvolvedNamespaces(); @@ -234,7 +293,7 @@ StatusWith<stdx::unordered_set<NamespaceString>> ViewCatalog::_validatePipeline_ // collection and a pipeline, but in this case we don't need this map to be accurate since // we will not be evaluating the pipeline. StringMap<ExpressionContext::ResolvedNamespace> resolvedNamespaces; - for (auto&& nss : liteParsedPipeline.getInvolvedNamespaces()) { + for (auto&& nss : involvedNamespaces) { resolvedNamespaces[nss.coll()] = {nss, {}}; } boost::intrusive_ptr<ExpressionContext> expCtx = @@ -254,6 +313,14 @@ StatusWith<stdx::unordered_set<NamespaceString>> ViewCatalog::_validatePipeline_ "$changeStream cannot be used in a view definition"}; } + if (serverGlobalParams.featureCompatibility.validateFeaturesAsMaster.load() && + !serverGlobalParams.featureCompatibility.isFullyUpgradedTo36()) { + auto status = validInViewUnder34FeatureCompatibility(expCtx, *pipelineStatus.getValue()); + if (!status.isOK()) { + return status; + } + } + return std::move(involvedNamespaces); } diff --git a/src/mongo/db/views/view_catalog_test.cpp b/src/mongo/db/views/view_catalog_test.cpp index 849d6d1845a..192e79a1255 100644 --- a/src/mongo/db/views/view_catalog_test.cpp +++ b/src/mongo/db/views/view_catalog_test.cpp @@ -179,6 +179,128 @@ TEST_F(ViewCatalogFixture, CreateViewOnDifferentDatabase) { viewCatalog.createView(opCtx.get(), viewName, viewOn, emptyPipeline, emptyCollation)); } +// TODO SERVER-31588: Remove FCV 3.4 validation during the 3.7 development cycle. +TEST_F(ViewCatalogFixture, CreateViewWith36FeaturesSucceedsUnder36FCV) { + EnsureFCV ensureFCV(EnsureFCV::Version::k36); + + const NamespaceString viewOn("db.coll"); + + ASSERT_OK(viewCatalog.createView(opCtx.get(), + NamespaceString("db.view1"), + viewOn, + BSON_ARRAY(BSON("$match" << BSON("$expr" << 1))), + emptyCollation)); + + ASSERT_OK(viewCatalog.createView( + opCtx.get(), + NamespaceString("db.view2"), + viewOn, + BSON_ARRAY(BSON("$match" << BSON("$jsonSchema" << BSON("required" << BSON_ARRAY("x"))))), + emptyCollation)); + + ASSERT_OK(viewCatalog.createView( + opCtx.get(), + NamespaceString("db.view3"), + viewOn, + BSON_ARRAY( + BSON("$facet" << BSON("output" << BSON_ARRAY(BSON("$match" << BSON("$expr" << 1)))))), + emptyCollation)); + + ASSERT_OK(viewCatalog.createView( + opCtx.get(), + NamespaceString("db.view4"), + viewOn, + BSON_ARRAY(BSON( + "$facet" << BSON( + "output" << BSON_ARRAY(BSON( + "$match" << BSON("$jsonSchema" << BSON("required" << BSON_ARRAY("x")))))))), + emptyCollation)); + + ASSERT_OK(viewCatalog.createView(opCtx.get(), + NamespaceString("db.view5"), + viewOn, + BSON_ARRAY(BSON("$lookup" << BSON("from" + << "fcoll" + << "as" + << "as" + << "pipeline" + << BSONArray()))), + emptyCollation)); +} + +// TODO SERVER-31588: Remove FCV 3.4 validation during the 3.7 development cycle. +TEST_F(ViewCatalogFixture, CreateViewWith36FeaturesFailsUnder34FCV) { + EnsureFCV ensureFCV(EnsureFCV::Version::k34); + + const NamespaceString viewName("db.view"); + const NamespaceString viewOn("db.coll"); + + ASSERT_THROWS_CODE(viewCatalog + .createView(opCtx.get(), + viewName, + viewOn, + BSON_ARRAY(BSON("$match" << BSON("$expr" << 1))), + emptyCollation) + .ignore(), + AssertionException, + ErrorCodes::QueryFeatureNotAllowed); + + ASSERT_THROWS_CODE( + viewCatalog + .createView(opCtx.get(), + viewName, + viewOn, + BSON_ARRAY(BSON("$match" << BSON("$jsonSchema" + << BSON("required" << BSON_ARRAY("x"))))), + emptyCollation) + .ignore(), + AssertionException, + ErrorCodes::QueryFeatureNotAllowed); + + ASSERT_THROWS_CODE( + viewCatalog + .createView(opCtx.get(), + viewName, + viewOn, + BSON_ARRAY(BSON("$facet" << BSON("output" << BSON_ARRAY(BSON( + "$match" << BSON("$expr" << 1)))))), + emptyCollation) + .ignore(), + AssertionException, + ErrorCodes::QueryFeatureNotAllowed); + + ASSERT_THROWS_CODE( + viewCatalog + .createView( + opCtx.get(), + viewName, + viewOn, + BSON_ARRAY(BSON( + "$facet" << BSON("output" << BSON_ARRAY(BSON( + "$match" << BSON("$jsonSchema" << BSON( + "required" << BSON_ARRAY("x")))))))), + emptyCollation) + .ignore(), + AssertionException, + ErrorCodes::QueryFeatureNotAllowed); + + ASSERT_THROWS_CODE(viewCatalog + .createView(opCtx.get(), + viewName, + viewOn, + BSON_ARRAY(BSON("$lookup" << BSON("from" + << "fcoll" + << "as" + << "as" + << "pipeline" + << BSONArray()))), + emptyCollation) + .ignore(), + AssertionException, + ErrorCodes::QueryFeatureNotAllowed); +} + + TEST_F(ViewCatalogFixture, CreateViewWithPipelineFailsOnInvalidStageName) { const NamespaceString viewName("db.view"); const NamespaceString viewOn("db.coll"); @@ -204,7 +326,7 @@ TEST_F(ReplViewCatalogFixture, CreateViewWithPipelineFailsOnIneligibleStage) { viewCatalog.createView(opCtx.get(), viewName, viewOn, invalidPipeline, emptyCollation) .ignore(), AssertionException, - 40255); + ErrorCodes::OptionNotSupportedOnView); } TEST_F(ViewCatalogFixture, CreateViewOnInvalidCollectionName) { @@ -383,7 +505,7 @@ TEST_F(ReplViewCatalogFixture, ModifyViewWithPipelineFailsOnIneligibleStage) { ASSERT_THROWS_CODE( viewCatalog.modifyView(opCtx.get(), viewName, viewOn, invalidPipeline).ignore(), AssertionException, - 40255); + ErrorCodes::OptionNotSupportedOnView); } TEST_F(ViewCatalogFixture, LookupMissingView) { |