mirror of https://github.com/mongodb/mongo
202 lines
7.2 KiB
JavaScript
202 lines
7.2 KiB
JavaScript
// @tags: [
|
|
// does_not_support_stepdowns,
|
|
// requires_fastcount,
|
|
// requires_non_retryable_commands,
|
|
// uses_map_reduce_with_temp_collections,
|
|
// requires_fcv_44,
|
|
// uses_$out,
|
|
// ]
|
|
|
|
/**
|
|
* Tests that various database commands respect the 'bypassDocumentValidation' flag:
|
|
*
|
|
* - aggregation with $out
|
|
* - applyOps (when not sharded)
|
|
* - findAndModify
|
|
* - insert
|
|
* - mapReduce
|
|
* - update
|
|
*/
|
|
(function() {
|
|
'use strict';
|
|
|
|
// For isWiredTiger.
|
|
load("jstests/concurrency/fsm_workload_helpers/server_types.js");
|
|
// For isReplSet
|
|
load("jstests/libs/fixture_helpers.js");
|
|
|
|
function assertFailsValidation(res) {
|
|
if (res instanceof WriteResult || res instanceof BulkWriteResult) {
|
|
assert.writeErrorWithCode(res, ErrorCodes.DocumentValidationFailure, tojson(res));
|
|
} else {
|
|
assert.commandFailedWithCode(res, ErrorCodes.DocumentValidationFailure, tojson(res));
|
|
}
|
|
}
|
|
|
|
const dbName = 'bypass_document_validation';
|
|
const collName = 'bypass_document_validation';
|
|
const myDb = db.getSiblingDB(dbName);
|
|
const coll = myDb[collName];
|
|
|
|
/**
|
|
* Tests that we can bypass document validation when appropriate when a collection has validator
|
|
* 'validator', which should enforce the existence of a field "a".
|
|
*/
|
|
function runBypassDocumentValidationTest(validator) {
|
|
// Use majority write concern to clear the drop-pending that can cause lock conflicts with
|
|
// transactions.
|
|
coll.drop({writeConcern: {w: "majority"}});
|
|
|
|
// Insert documents into the collection that would not be valid before setting 'validator'.
|
|
assert.commandWorked(coll.insert({_id: 1}));
|
|
assert.commandWorked(coll.insert({_id: 2}));
|
|
assert.commandWorked(myDb.runCommand({collMod: collName, validator: validator}));
|
|
|
|
const isMongos = db.runCommand({isdbgrid: 1}).isdbgrid;
|
|
// Test applyOps with a simple insert if not on mongos.
|
|
if (!isMongos) {
|
|
const op = [{op: 'i', ns: coll.getFullName(), o: {_id: 9}}];
|
|
assertFailsValidation(myDb.runCommand({applyOps: op, bypassDocumentValidation: false}));
|
|
assert.eq(0, coll.count({_id: 9}));
|
|
assert.commandWorked(myDb.runCommand({applyOps: op, bypassDocumentValidation: true}));
|
|
assert.eq(1, coll.count({_id: 9}));
|
|
}
|
|
|
|
// Test the aggregation command with a $out stage.
|
|
const outputCollName = 'bypass_output_coll';
|
|
const outputColl = myDb[outputCollName];
|
|
outputColl.drop();
|
|
assert.commandWorked(myDb.createCollection(outputCollName, {validator: validator}));
|
|
const pipeline =
|
|
[{$match: {_id: 1}}, {$project: {aggregation: {$add: [1]}}}, {$out: outputCollName}];
|
|
assert.throws(function() {
|
|
coll.aggregate(pipeline, {bypassDocumentValidation: false});
|
|
});
|
|
assert.eq(0, outputColl.count({aggregation: 1}));
|
|
coll.aggregate(pipeline, {bypassDocumentValidation: true});
|
|
assert.eq(1, outputColl.count({aggregation: 1}));
|
|
|
|
// Test the findAndModify command.
|
|
assert.throws(function() {
|
|
coll.findAndModify({update: {$set: {findAndModify: 1}}, bypassDocumentValidation: false});
|
|
});
|
|
assert.eq(0, coll.count({findAndModify: 1}));
|
|
coll.findAndModify({update: {$set: {findAndModify: 1}}, bypassDocumentValidation: true});
|
|
assert.eq(1, coll.count({findAndModify: 1}));
|
|
|
|
// Test the mapReduce command.
|
|
const map = function() {
|
|
emit(1, 1);
|
|
};
|
|
const reduce = function() {
|
|
return 'mapReduce';
|
|
};
|
|
let res = myDb.runCommand({
|
|
mapReduce: collName,
|
|
map: map,
|
|
reduce: reduce,
|
|
out: {replace: outputCollName},
|
|
bypassDocumentValidation: false
|
|
});
|
|
assertFailsValidation(res);
|
|
assert.eq(0, outputColl.count({value: 'mapReduce'}));
|
|
res = myDb.runCommand({
|
|
mapReduce: collName,
|
|
map: map,
|
|
reduce: reduce,
|
|
out: {replace: outputCollName},
|
|
bypassDocumentValidation: true
|
|
});
|
|
assert.commandWorked(res);
|
|
assert.eq(1, outputColl.count({value: 'mapReduce'}));
|
|
|
|
// Test the mapReduce command if it is reading from a different database and collection without
|
|
// validation.
|
|
const otherDb = myDb.getSisterDB("mr_second_input_db");
|
|
const otherDbColl = otherDb.mr_second_input_coll;
|
|
assert.commandWorked(otherDbColl.insert({val: 1}));
|
|
outputColl.drop();
|
|
assert.commandWorked(myDb.createCollection(outputCollName, {validator: validator}));
|
|
res = otherDb.runCommand({
|
|
mapReduce: otherDbColl.getName(),
|
|
map: map,
|
|
reduce: reduce,
|
|
out: {replace: outputCollName, db: myDb.getName()},
|
|
bypassDocumentValidation: false
|
|
});
|
|
assertFailsValidation(res);
|
|
assert.eq(0, outputColl.count({value: 'mapReduce'}));
|
|
res = otherDb.runCommand({
|
|
mapReduce: otherDbColl.getName(),
|
|
map: map,
|
|
reduce: reduce,
|
|
out: {replace: outputCollName, db: myDb.getName()},
|
|
bypassDocumentValidation: true
|
|
});
|
|
assert.commandWorked(res);
|
|
assert.eq(1, outputColl.count({value: 'mapReduce'}));
|
|
// Test the insert command. Includes a test for a document with no _id (SERVER-20859).
|
|
res = myDb.runCommand({insert: collName, documents: [{}], bypassDocumentValidation: false});
|
|
assertFailsValidation(BulkWriteResult(res));
|
|
res = myDb.runCommand(
|
|
{insert: collName, documents: [{}, {_id: 6}], bypassDocumentValidation: false});
|
|
assertFailsValidation(BulkWriteResult(res));
|
|
res = myDb.runCommand(
|
|
{insert: collName, documents: [{}, {_id: 6}], bypassDocumentValidation: true});
|
|
assert.commandWorked(res);
|
|
|
|
// Test the update command.
|
|
res = myDb.runCommand({
|
|
update: collName,
|
|
updates: [{q: {}, u: {$set: {update: 1}}}],
|
|
bypassDocumentValidation: false
|
|
});
|
|
assertFailsValidation(BulkWriteResult(res));
|
|
assert.eq(0, coll.count({update: 1}));
|
|
res = myDb.runCommand({
|
|
update: collName,
|
|
updates: [{q: {}, u: {$set: {update: 1}}}],
|
|
bypassDocumentValidation: true
|
|
});
|
|
assert.commandWorked(res);
|
|
assert.eq(1, coll.count({update: 1}));
|
|
|
|
// Pipeline-style update is only supported for commands and not for OP_UPDATE which cannot
|
|
// differentiate between an update object and an array.
|
|
res = myDb.runCommand({
|
|
update: collName,
|
|
updates: [{q: {}, u: [{$set: {pipeline: 1}}]}],
|
|
bypassDocumentValidation: false
|
|
});
|
|
assertFailsValidation(BulkWriteResult(res));
|
|
assert.eq(0, coll.count({pipeline: 1}));
|
|
|
|
assert.commandWorked(myDb.runCommand({
|
|
update: collName,
|
|
updates: [{q: {}, u: [{$set: {pipeline: 1}}]}],
|
|
bypassDocumentValidation: true
|
|
}));
|
|
assert.eq(1, coll.count({pipeline: 1}));
|
|
|
|
assert.commandFailed(myDb.runCommand({
|
|
findAndModify: collName,
|
|
update: [{$set: {findAndModifyPipeline: 1}}],
|
|
bypassDocumentValidation: false
|
|
}));
|
|
assert.eq(0, coll.count({findAndModifyPipeline: 1}));
|
|
|
|
assert.commandWorked(myDb.runCommand({
|
|
findAndModify: collName,
|
|
update: [{$set: {findAndModifyPipeline: 1}}],
|
|
bypassDocumentValidation: true
|
|
}));
|
|
assert.eq(1, coll.count({findAndModifyPipeline: 1}));
|
|
}
|
|
|
|
// Run the test using a normal validator.
|
|
runBypassDocumentValidationTest({a: {$exists: true}});
|
|
|
|
// Run the test again with an equivalent JSON Schema validator.
|
|
runBypassDocumentValidationTest({$jsonSchema: {required: ['a']}});
|
|
})();
|