summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMihai Andrei <mihai.andrei@10gen.com>2020-08-17 11:20:35 -0400
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-09-11 14:15:15 +0000
commit94e5a1620a00997e053f5d9cc75be65aa8b73016 (patch)
tree1617d94703923ab8df855d7e2b19f5f892347ba5
parenta70ac5e30ee48259173ad172c1ca38999871181d (diff)
downloadmongo-94e5a1620a00997e053f5d9cc75be65aa8b73016.tar.gz
SERVER-49447 Implement validation error generation for jsonSchema scalar/miscellaneous keywords
-rw-r--r--src/mongo/db/matcher/doc_validation_error.cpp103
-rw-r--r--src/mongo/db/matcher/doc_validation_error_json_schema_test.cpp439
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_fmod.cpp10
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_fmod.h8
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_max_items.h11
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_max_length.h15
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_min_items.h11
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_min_length.h15
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_num_array_items.cpp10
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_num_array_items.h3
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_str_length.cpp12
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_str_length.h3
-rw-r--r--src/mongo/db/matcher/schema/json_schema_parser.cpp39
13 files changed, 622 insertions, 57 deletions
diff --git a/src/mongo/db/matcher/doc_validation_error.cpp b/src/mongo/db/matcher/doc_validation_error.cpp
index bbc2f4eb1cb..0175172f3b5 100644
--- a/src/mongo/db/matcher/doc_validation_error.cpp
+++ b/src/mongo/db/matcher/doc_validation_error.cpp
@@ -44,7 +44,11 @@
#include "mongo/db/matcher/expression_type.h"
#include "mongo/db/matcher/expression_visitor.h"
#include "mongo/db/matcher/match_expression_walker.h"
+#include "mongo/db/matcher/schema/expression_internal_schema_fmod.h"
+#include "mongo/db/matcher/schema/expression_internal_schema_max_length.h"
+#include "mongo/db/matcher/schema/expression_internal_schema_min_length.h"
#include "mongo/db/matcher/schema/expression_internal_schema_object_match.h"
+#include "mongo/db/matcher/schema/expression_internal_schema_str_length.h"
namespace mongo::doc_validation_error {
namespace {
@@ -356,17 +360,65 @@ public:
void visit(const InternalExprEqMatchExpression* expr) final {}
void visit(const InternalSchemaAllElemMatchFromIndexMatchExpression* expr) final {}
void visit(const InternalSchemaAllowedPropertiesMatchExpression* expr) final {}
- void visit(const InternalSchemaBinDataEncryptedTypeExpression* expr) final {}
- void visit(const InternalSchemaBinDataSubTypeExpression* expr) final {}
+ void visit(const InternalSchemaBinDataEncryptedTypeExpression* expr) final {
+ static constexpr auto kNormalReason = "encrypted value has wrong type";
+ _context->pushNewFrame(*expr, _context->getCurrentDocument());
+ if (_context->shouldGenerateError(*expr)) {
+ ElementPath path(expr->path(), LeafArrayBehavior::kNoTraversal);
+ BSONMatchableDocument doc(_context->getCurrentDocument());
+ MatchableDocument::IteratorHolder cursor(&doc, &path);
+ invariant(cursor->more());
+ auto elem = cursor->next().element();
+ // Only generate an error in the normal case since if the value exists and it is
+ // encrypted, in the inverted case, this node's sibling expression will generate an
+ // appropriate error.
+ if (elem.type() == BSONType::BinData && elem.binDataType() == BinDataType::Encrypt &&
+ _context->inversion == InvertError::kNormal) {
+ auto& builder = _context->getCurrentObjBuilder();
+ appendOperatorName(*expr->getErrorAnnotation(), &builder);
+ builder.append("reason", kNormalReason);
+ } else {
+ _context->setCurrentRuntimeState(RuntimeState::kNoError);
+ }
+ }
+ }
+ void visit(const InternalSchemaBinDataSubTypeExpression* expr) final {
+ static constexpr auto kNormalReason = "value was not encrypted";
+ static constexpr auto kInvertedReason = "value was encrypted";
+ _context->pushNewFrame(*expr, _context->getCurrentDocument());
+ if (_context->shouldGenerateError(*expr)) {
+ auto& builder = _context->getCurrentObjBuilder();
+ appendOperatorName(*expr->getErrorAnnotation(), &builder);
+ appendErrorReason(*expr, kNormalReason, kInvertedReason);
+ }
+ }
void visit(const InternalSchemaCondMatchExpression* expr) final {}
void visit(const InternalSchemaEqMatchExpression* expr) final {}
- void visit(const InternalSchemaFmodMatchExpression* expr) final {}
+ void visit(const InternalSchemaFmodMatchExpression* expr) final {
+ static constexpr auto kNormalReason =
+ "considered value is not a multiple of the specified value";
+ static constexpr auto kInvertedReason =
+ "considered value is a multiple of the specified value";
+ static const std::set<BSONType> kExpectedTypes{BSONType::NumberLong,
+ BSONType::NumberDouble,
+ BSONType::NumberDecimal,
+ BSONType::NumberInt};
+ generatePathError(*expr,
+ kNormalReason,
+ kInvertedReason,
+ &kExpectedTypes,
+ LeafArrayBehavior::kNoTraversal);
+ }
void visit(const InternalSchemaMatchArrayIndexMatchExpression* expr) final {}
void visit(const InternalSchemaMaxItemsMatchExpression* expr) final {}
- void visit(const InternalSchemaMaxLengthMatchExpression* expr) final {}
+ void visit(const InternalSchemaMaxLengthMatchExpression* expr) final {
+ generateStringLengthError(*expr);
+ }
void visit(const InternalSchemaMaxPropertiesMatchExpression* expr) final {}
void visit(const InternalSchemaMinItemsMatchExpression* expr) final {}
- void visit(const InternalSchemaMinLengthMatchExpression* expr) final {}
+ void visit(const InternalSchemaMinLengthMatchExpression* expr) final {
+ generateStringLengthError(*expr);
+ }
void visit(const InternalSchemaMinPropertiesMatchExpression* expr) final {}
void visit(const InternalSchemaObjectMatchExpression* expr) final {
// This node should never be responsible for generating an error directly.
@@ -414,9 +466,11 @@ public:
void visit(const ModMatchExpression* expr) final {
static constexpr auto kNormalReason = "$mod did not evaluate to expected remainder";
static constexpr auto kInvertedReason = "$mod did evaluate to expected remainder";
- static const std::set<BSONType> expectedTypes{
- NumberLong, NumberDouble, NumberDecimal, NumberInt};
- generatePathError(*expr, kNormalReason, kInvertedReason, &expectedTypes);
+ static const std::set<BSONType> kExpectedTypes{BSONType::NumberLong,
+ BSONType::NumberDouble,
+ BSONType::NumberDecimal,
+ BSONType::NumberInt};
+ generatePathError(*expr, kNormalReason, kInvertedReason, &kExpectedTypes);
}
void visit(const NorMatchExpression* expr) final {
preVisitTreeOperator(expr);
@@ -442,8 +496,9 @@ public:
void visit(const RegexMatchExpression* expr) final {
static constexpr auto kNormalReason = "regular expression did not match";
static constexpr auto kInvertedReason = "regular expression did match";
- static const std::set<BSONType> expectedTypes{String, Symbol, RegEx};
- generatePathError(*expr, kNormalReason, kInvertedReason, &expectedTypes);
+ static const std::set<BSONType> kExpectedTypes{
+ BSONType::String, BSONType::Symbol, BSONType::RegEx};
+ generatePathError(*expr, kNormalReason, kInvertedReason, &kExpectedTypes);
}
void visit(const SizeMatchExpression* expr) final {
static constexpr auto kNormalReason = "array length was not equal to given size";
@@ -713,6 +768,14 @@ private:
}
}
+ void generateStringLengthError(const InternalSchemaStrLengthMatchExpression& expr) {
+ static constexpr auto kNormalReason = "specified string length was not satisfied";
+ static constexpr auto kInvertedReason = "specified string length was satisfied";
+ static const std::set<BSONType> expectedTypes{BSONType::String};
+ generatePathError(
+ expr, kNormalReason, kInvertedReason, &expectedTypes, LeafArrayBehavior::kNoTraversal);
+ }
+
ValidationErrorContext* _context;
};
@@ -873,17 +936,27 @@ public:
void visit(const InternalExprEqMatchExpression* expr) final {}
void visit(const InternalSchemaAllElemMatchFromIndexMatchExpression* expr) final {}
void visit(const InternalSchemaAllowedPropertiesMatchExpression* expr) final {}
- void visit(const InternalSchemaBinDataEncryptedTypeExpression* expr) final {}
- void visit(const InternalSchemaBinDataSubTypeExpression* expr) final {}
+ void visit(const InternalSchemaBinDataEncryptedTypeExpression* expr) final {
+ _context->finishCurrentError(expr);
+ }
+ void visit(const InternalSchemaBinDataSubTypeExpression* expr) final {
+ _context->finishCurrentError(expr);
+ }
void visit(const InternalSchemaCondMatchExpression* expr) final {}
void visit(const InternalSchemaEqMatchExpression* expr) final {}
- void visit(const InternalSchemaFmodMatchExpression* expr) final {}
+ void visit(const InternalSchemaFmodMatchExpression* expr) final {
+ _context->finishCurrentError(expr);
+ }
void visit(const InternalSchemaMatchArrayIndexMatchExpression* expr) final {}
void visit(const InternalSchemaMaxItemsMatchExpression* expr) final {}
- void visit(const InternalSchemaMaxLengthMatchExpression* expr) final {}
+ void visit(const InternalSchemaMaxLengthMatchExpression* expr) final {
+ _context->finishCurrentError(expr);
+ }
void visit(const InternalSchemaMaxPropertiesMatchExpression* expr) final {}
void visit(const InternalSchemaMinItemsMatchExpression* expr) final {}
- void visit(const InternalSchemaMinLengthMatchExpression* expr) final {}
+ void visit(const InternalSchemaMinLengthMatchExpression* expr) final {
+ _context->finishCurrentError(expr);
+ }
void visit(const InternalSchemaMinPropertiesMatchExpression* expr) final {}
void visit(const InternalSchemaObjectMatchExpression* expr) final {
_context->finishCurrentError(expr);
diff --git a/src/mongo/db/matcher/doc_validation_error_json_schema_test.cpp b/src/mongo/db/matcher/doc_validation_error_json_schema_test.cpp
index 7de748f1f43..9af4f7f0a60 100644
--- a/src/mongo/db/matcher/doc_validation_error_json_schema_test.cpp
+++ b/src/mongo/db/matcher/doc_validation_error_json_schema_test.cpp
@@ -440,5 +440,444 @@ TEST(JSONSchemaValidation, BSONTypeNoImplicitArrayTraversal) {
doc_validation_error::verifyGeneratedError(query, document, expectedError);
}
+
+// Scalar keywords
+
+// minLength
+TEST(JSONSchemaValidation, BasicMinLength) {
+ BSONObj query =
+ fromjson("{'$jsonSchema': {'properties': {'a': {'type': 'string','minLength': 4}}}}");
+ BSONObj document = fromjson("{'a': 'foo'}");
+ BSONObj expectedError = fromjson(
+ "{'operatorName': '$jsonSchema',"
+ "'schemaRulesNotSatisfied': ["
+ " {'operatorName': 'properties', 'propertiesNotSatisfied': ["
+ " {'propertyName': 'a', 'details': ["
+ " {'operatorName': 'minLength',"
+ " 'specifiedAs': {'minLength': 4},"
+ " 'reason': 'specified string length was not satisfied',"
+ " 'consideredValue': 'foo'}]}]}]}");
+ doc_validation_error::verifyGeneratedError(query, document, expectedError);
+}
+
+TEST(JSONSchemaValidation, MinLengthNoExplicitType) {
+ BSONObj query = fromjson("{'$jsonSchema': {'properties': {'a': {'minLength': 4}}}}");
+ BSONObj document = fromjson("{'a': 'foo'}");
+ BSONObj expectedError = fromjson(
+ "{'operatorName': '$jsonSchema',"
+ "'schemaRulesNotSatisfied': ["
+ " {'operatorName': 'properties', 'propertiesNotSatisfied': ["
+ " {'propertyName': 'a', 'details': ["
+ " {'operatorName': 'minLength',"
+ " 'specifiedAs': {'minLength': 4},"
+ " 'reason': 'specified string length was not satisfied',"
+ " 'consideredValue': 'foo'}]}]}]}");
+ doc_validation_error::verifyGeneratedError(query, document, expectedError);
+}
+
+TEST(JSONSchemaValidation, MinLengthNonString) {
+ BSONObj query =
+ fromjson("{'$jsonSchema': {'properties': {'a': {'type': 'string','minLength': 4}}}}");
+ BSONObj document = fromjson("{'a': 1}");
+ BSONObj expectedError = fromjson(
+ "{'operatorName': '$jsonSchema', 'schemaRulesNotSatisfied': ["
+ " {'operatorName': 'properties', 'propertiesNotSatisfied': ["
+ " {'propertyName': 'a', 'details': ["
+ " {'operatorName': 'minLength', "
+ " 'specifiedAs': { 'minLength': 4 }, "
+ " 'reason': 'type did not match', "
+ " 'consideredType': 'int', "
+ " 'expectedType': 'string', "
+ " 'consideredValue': 1 }, "
+ " {'operatorName': 'type', "
+ " 'specifiedAs': { 'type': 'string' }, "
+ " 'reason': 'type did not match', "
+ " 'consideredValue': 1, "
+ " 'consideredType': 'int' }]}]}]}");
+ doc_validation_error::verifyGeneratedError(query, document, expectedError);
+}
+
+TEST(JSONSchemaValidation, MinLengthNested) {
+ BSONObj query = fromjson(
+ "{'$jsonSchema': {"
+ " 'properties': {"
+ " 'a': {'properties': "
+ " {'b': {'type': 'string', 'minLength': 4}}}}}}}}");
+ BSONObj document = fromjson("{'a': {'b':'foo'}}");
+ BSONObj expectedError = fromjson(
+ "{'operatorName': '$jsonSchema',"
+ " 'schemaRulesNotSatisfied': ["
+ " {'operatorName': 'properties',"
+ " 'propertiesNotSatisfied': ["
+ " {'propertyName': 'a', "
+ " 'details':"
+ " [{'operatorName': 'properties',"
+ " 'propertiesNotSatisfied': ["
+ " {'propertyName': 'b', "
+ " 'details': ["
+ " {'operatorName': 'minLength',"
+ " 'specifiedAs': {'minLength': 4},"
+ " 'reason': 'specified string length was not "
+ "satisfied',"
+ " 'consideredValue': 'foo'}]}]}]}]}]}");
+ doc_validation_error::verifyGeneratedError(query, document, expectedError);
+}
+
+TEST(JSONSchemaValidation, MinLengthAtTopLevelHasNoEffect) {
+ BSONObj query = fromjson("{'$nor': [{'$jsonSchema': {'minLength': 1}}]}");
+ BSONObj document = fromjson("{a: 'foo'}");
+ BSONObj expectedError = fromjson(
+ "{'operatorName': '$nor',"
+ " 'clausesNotSatisfied': [{'index': 0, 'details': "
+ " {'operatorName': '$jsonSchema',"
+ " 'schemaRulesNotSatisfied': ["
+ " {'operatorName': 'minLength', "
+ " 'specifiedAs': {'minLength': 1}, "
+ " 'reason': 'expression always evaluates to true'}]}}]}");
+ doc_validation_error::verifyGeneratedError(query, document, expectedError);
+}
+
+// maxLength
+TEST(JSONSchemaValidation, BasicMaxLength) {
+ BSONObj query =
+ fromjson("{'$jsonSchema': {'properties': {'a': {'type': 'string','maxLength': 4}}}}");
+ BSONObj document = fromjson("{'a': 'foo, bar, baz'}");
+ BSONObj expectedError = fromjson(
+ "{'operatorName': '$jsonSchema',"
+ "'schemaRulesNotSatisfied': ["
+ " {'operatorName': 'properties', 'propertiesNotSatisfied': ["
+ " {'propertyName': 'a', 'details': ["
+ " {'operatorName': 'maxLength',"
+ " 'specifiedAs': {'maxLength': 4},"
+ " 'reason': 'specified string length was not satisfied',"
+ " 'consideredValue': 'foo, bar, baz'}]}]}]}");
+ doc_validation_error::verifyGeneratedError(query, document, expectedError);
+}
+
+TEST(JSONSchemaValidation, MaxLengthNoExplicitType) {
+ BSONObj query = fromjson("{'$jsonSchema': {'properties': {'a': {'maxLength': 4}}}}");
+ BSONObj document = fromjson("{'a': 'foo, bar, baz'}");
+ BSONObj expectedError = fromjson(
+ "{'operatorName': '$jsonSchema',"
+ "'schemaRulesNotSatisfied': ["
+ " {'operatorName': 'properties', 'propertiesNotSatisfied': ["
+ " {'propertyName': 'a', 'details': ["
+ " {'operatorName': 'maxLength',"
+ " 'specifiedAs': {'maxLength': 4},"
+ " 'reason': 'specified string length was not satisfied',"
+ " 'consideredValue': 'foo, bar, baz'}]}]}]}");
+ doc_validation_error::verifyGeneratedError(query, document, expectedError);
+}
+
+TEST(JSONSchemaValidation, MaxLengthNonString) {
+ BSONObj query =
+ fromjson("{'$jsonSchema': {'properties': {'a': {'type': 'string','maxLength': 4}}}}");
+ BSONObj document = fromjson("{'a': 1}");
+ BSONObj expectedError = fromjson(
+ "{'operatorName': '$jsonSchema', 'schemaRulesNotSatisfied': ["
+ " {'operatorName': 'properties', 'propertiesNotSatisfied': ["
+ " {'propertyName': 'a', 'details': ["
+ " {'operatorName': 'maxLength', "
+ " 'specifiedAs': { 'maxLength': 4 }, "
+ " 'reason': 'type did not match', "
+ " 'consideredType': 'int', "
+ " 'expectedType': 'string', "
+ " 'consideredValue': 1 }, "
+ " {'operatorName': 'type', "
+ " 'specifiedAs': { 'type': 'string' }, "
+ " 'reason': 'type did not match', "
+ " 'consideredValue': 1, "
+ " 'consideredType': 'int' }]}]}]}");
+ doc_validation_error::verifyGeneratedError(query, document, expectedError);
+}
+
+TEST(JSONSchemaValidation, MaxLengthNested) {
+ BSONObj query = fromjson(
+ "{'$jsonSchema': {"
+ " 'properties': {"
+ " 'a': {'properties': "
+ " {'b': {'type': 'string', 'maxLength': 4}}}}}}}}");
+ BSONObj document = fromjson("{'a': {'b': 'foo, bar, baz'}}");
+ BSONObj expectedError = fromjson(
+ "{'operatorName': '$jsonSchema',"
+ " 'schemaRulesNotSatisfied': ["
+ " {'operatorName': 'properties',"
+ " 'propertiesNotSatisfied': ["
+ " {'propertyName': 'a', "
+ " 'details':"
+ " [{'operatorName': 'properties',"
+ " 'propertiesNotSatisfied': ["
+ " {'propertyName': 'b', "
+ " 'details': ["
+ " {'operatorName': 'maxLength',"
+ " 'specifiedAs': {'maxLength': 4},"
+ " 'reason': 'specified string length was not "
+ "satisfied',"
+ " 'consideredValue': 'foo, bar, baz'}]}]}]}]}]}");
+ doc_validation_error::verifyGeneratedError(query, document, expectedError);
+}
+
+TEST(JSONSchemaValidation, MaxLengthAtTopLevelHasNoEffect) {
+ BSONObj query = fromjson("{'$nor': [{'$jsonSchema': {'maxLength': 1000}}]}");
+ BSONObj document = fromjson("{a: 'foo'}");
+ BSONObj expectedError = fromjson(
+ "{'operatorName': '$nor',"
+ " 'clausesNotSatisfied': [{'index': 0, 'details': "
+ " {'operatorName': '$jsonSchema',"
+ " 'schemaRulesNotSatisfied': ["
+ " {'operatorName': 'maxLength', "
+ " 'specifiedAs': {'maxLength': 1000}, "
+ " 'reason': 'expression always evaluates to true'}]}}]}");
+ doc_validation_error::verifyGeneratedError(query, document, expectedError);
+}
+
+// pattern
+TEST(JSONSchemaValidation, BasicPattern) {
+ BSONObj query =
+ fromjson("{'$jsonSchema': {'properties': {'a': {'type': 'string','pattern': '^S'}}}}");
+ BSONObj document = fromjson("{'a': 'slow'}");
+ BSONObj expectedError = fromjson(
+ "{'operatorName': '$jsonSchema',"
+ "'schemaRulesNotSatisfied': ["
+ " {'operatorName': 'properties', 'propertiesNotSatisfied': ["
+ " {'propertyName': 'a', 'details': ["
+ " {'operatorName': 'pattern',"
+ " 'specifiedAs': {'pattern': '^S'},"
+ " 'reason': 'regular expression did not match',"
+ " 'consideredValue': 'slow'}]}]}]}");
+ doc_validation_error::verifyGeneratedError(query, document, expectedError);
+}
+
+TEST(JSONSchemaValidation, PatternNoExplicitType) {
+ BSONObj query = fromjson("{'$jsonSchema': {'properties': {'a': {'pattern': '^S'}}}}");
+ BSONObj document = fromjson("{'a': 'slow'}");
+ BSONObj expectedError = fromjson(
+ "{'operatorName': '$jsonSchema',"
+ "'schemaRulesNotSatisfied': ["
+ " {'operatorName': 'properties', 'propertiesNotSatisfied': ["
+ " {'propertyName': 'a', 'details': ["
+ " {'operatorName': 'pattern',"
+ " 'specifiedAs': {'pattern': '^S'},"
+ " 'reason': 'regular expression did not match',"
+ " 'consideredValue': 'slow'}]}]}]}");
+ doc_validation_error::verifyGeneratedError(query, document, expectedError);
+}
+
+TEST(JSONSchemaValidation, PatternNonString) {
+ BSONObj query =
+ fromjson("{'$jsonSchema': {'properties': {'a': {'type': 'string','pattern': '^S'}}}}");
+ BSONObj document = fromjson("{'a': 1}");
+ BSONObj expectedError = fromjson(
+ "{'operatorName': '$jsonSchema', 'schemaRulesNotSatisfied': ["
+ " { operatorName: 'properties', 'propertiesNotSatisfied': ["
+ " {propertyName: 'a', 'details': ["
+ " {'operatorName': 'pattern',"
+ " 'specifiedAs': { pattern: '^S' }, "
+ " 'reason': 'type did not match', "
+ " 'consideredType': 'int', "
+ " 'expectedTypes': [ 'regex', 'string', 'symbol' ], "
+ " 'consideredValue': 1 }, "
+ " {'operatorName': 'type', "
+ " 'specifiedAs': { 'type': 'string' }, "
+ " 'reason': 'type did not match', "
+ " 'consideredValue': 1, "
+ " 'consideredType': 'int'}]}]}]}");
+ doc_validation_error::verifyGeneratedError(query, document, expectedError);
+}
+
+TEST(JSONSchemaValidation, PatternNested) {
+ BSONObj query = fromjson(
+ "{'$jsonSchema': {"
+ " 'properties': {"
+ " 'a': {'properties': "
+ " {'b': {'type': 'string', 'pattern': '^S'}}}}}}}}");
+ BSONObj document = fromjson("{'a': {'b': 'foo'}}");
+ BSONObj expectedError = fromjson(
+ "{'operatorName': '$jsonSchema',"
+ " 'schemaRulesNotSatisfied': ["
+ " {'operatorName': 'properties',"
+ " 'propertiesNotSatisfied': ["
+ " {'propertyName': 'a', "
+ " 'details':"
+ " [{'operatorName': 'properties',"
+ " 'propertiesNotSatisfied': ["
+ " {'propertyName': 'b', "
+ " 'details': ["
+ " {'operatorName': 'pattern',"
+ " 'specifiedAs': {'pattern': '^S'},"
+ " 'reason': 'regular expression did not match',"
+ " 'consideredValue': 'foo'}]}]}]}]}]}");
+ doc_validation_error::verifyGeneratedError(query, document, expectedError);
+}
+
+TEST(JSONSchemaValidation, PatternAtTopLevelHasNoEffect) {
+ BSONObj query = fromjson("{'$nor': [{'$jsonSchema': {'pattern': '^S'}}]}");
+ BSONObj document = fromjson("{a: 'Slow'}");
+ BSONObj expectedError = fromjson(
+ "{'operatorName': '$nor',"
+ " 'clausesNotSatisfied': [{'index': 0, 'details': "
+ " {'operatorName': '$jsonSchema',"
+ " 'schemaRulesNotSatisfied': ["
+ " {'operatorName': 'pattern', "
+ " 'specifiedAs': {'pattern': '^S'}, "
+ " 'reason': 'expression always evaluates to true'}]}}]}");
+ doc_validation_error::verifyGeneratedError(query, document, expectedError);
+}
+
+// multipleOf
+TEST(JSONSchemaValidation, BasicMultipleOf) {
+ BSONObj query =
+ fromjson("{'$jsonSchema':{properties: {a: {type: 'number', multipleOf: 2.1}}}}");
+ BSONObj document = fromjson("{'a': 1.1}");
+ BSONObj expectedError = fromjson(
+ "{'operatorName': '$jsonSchema',"
+ " 'schemaRulesNotSatisfied': ["
+ " {'operatorName': 'properties',"
+ " 'propertiesNotSatisfied': ["
+ " {'propertyName': 'a', details: ["
+ " {'operatorName': 'multipleOf',"
+ " 'specifiedAs': {'multipleOf': 2.1},"
+ " 'reason': 'considered value is not a multiple of the specified "
+ "value',"
+ " 'consideredValue': 1.1}]}]}]}");
+ doc_validation_error::verifyGeneratedError(query, document, expectedError);
+}
+
+TEST(JSONSchemaValidation, MultipleOfNoExplicitType) {
+ BSONObj query = fromjson("{'$jsonSchema':{properties: {a: {multipleOf: 2.1}}}}");
+ BSONObj document = fromjson("{'a': 1.1}");
+ BSONObj expectedError = fromjson(
+ "{'operatorName': '$jsonSchema',"
+ " 'schemaRulesNotSatisfied': ["
+ " {'operatorName': 'properties',"
+ " 'propertiesNotSatisfied': ["
+ " {'propertyName': 'a', details: ["
+ " {'operatorName': 'multipleOf',"
+ " 'specifiedAs': {'multipleOf': 2.1},"
+ " 'reason': 'considered value is not a multiple of the specified "
+ "value',"
+ " 'consideredValue': 1.1}]}]}]}");
+ doc_validation_error::verifyGeneratedError(query, document, expectedError);
+}
+
+TEST(JSONSchemaValidation, MultipleOfNonNumeric) {
+ BSONObj query =
+ fromjson("{'$jsonSchema':{properties: {a: {type: 'number', 'multipleOf': 2.1}}}}");
+ BSONObj document = fromjson("{'a': 'foo'}");
+ BSONObj expectedError = fromjson(
+ "{'operatorName': '$jsonSchema', 'schemaRulesNotSatisfied': ["
+ " {'operatorName': 'properties', 'propertiesNotSatisfied': ["
+ " {'propertyName': 'a', 'details': ["
+ " {'operatorName': 'multipleOf', "
+ " 'specifiedAs': { 'multipleOf': 2.1 }, "
+ " 'reason': 'type did not match', "
+ " 'consideredType': 'string', "
+ " 'expectedTypes': ['decimal', 'double', 'int', 'long'], "
+ " 'consideredValue': 'foo' }, "
+ " {'operatorName': 'type', "
+ " 'specifiedAs': { 'type': 'number' }, "
+ " 'reason': 'type did not match', "
+ " 'consideredValue': 'foo', "
+ " 'consideredType': 'string' }]}]}]}");
+ doc_validation_error::verifyGeneratedError(query, document, expectedError);
+}
+
+TEST(JSONSchemaValidation, MultipleOfNested) {
+ BSONObj query = fromjson(
+ "{'$jsonSchema': {"
+ " 'properties': {"
+ " 'a': {'properties': {'b': {'multipleOf': 2.1}}}}}}}");
+ BSONObj document = fromjson("{'a': {'b': 1}}");
+ BSONObj expectedError = fromjson(
+ "{'operatorName': '$jsonSchema',"
+ " 'schemaRulesNotSatisfied': ["
+ " {'operatorName': 'properties',"
+ " 'propertiesNotSatisfied': ["
+ " {'propertyName': 'a', "
+ " 'details':"
+ " [{'operatorName': 'properties',"
+ " 'propertiesNotSatisfied': ["
+ " {'propertyName': 'b', "
+ " 'details': ["
+ " {'operatorName': 'multipleOf',"
+ " 'specifiedAs': {'multipleOf': 2.1},"
+ " 'reason': 'considered value is not a multiple of "
+ "the specified value',"
+ " 'consideredValue': 1}]}]}]}]}]}");
+ doc_validation_error::verifyGeneratedError(query, document, expectedError);
+ doc_validation_error::verifyGeneratedError(query, document, expectedError);
+}
+
+TEST(JSONSchemaValidation, MultipleOfAtTopLevelHasNoEffect) {
+ BSONObj query = fromjson("{'$nor': [{'$jsonSchema': {'multipleOf': 1}}]}");
+ BSONObj document = fromjson("{a: 'foo'}");
+ BSONObj expectedError = fromjson(
+ "{'operatorName': '$nor',"
+ " 'clausesNotSatisfied': [{'index': 0, 'details': "
+ " {'operatorName': '$jsonSchema',"
+ " 'schemaRulesNotSatisfied': ["
+ " {'operatorName': 'multipleOf', "
+ " 'specifiedAs': {'multipleOf': 1}, "
+ " 'reason': 'expression always evaluates to true'}]}}]}");
+ doc_validation_error::verifyGeneratedError(query, document, expectedError);
+}
+
+// encrypt
+TEST(JSONSchemaValidation, BasicEncrypt) {
+ BSONObj query =
+ fromjson("{'$jsonSchema': {bsonType: 'object', properties: {a: {encrypt: {}}}}}");
+ BSONObj document = BSON("a" << BSONBinData("abc", 3, BinDataType::BinDataGeneral));
+ BSONObj expectedError = fromjson(
+ "{'operatorName': '$jsonSchema',"
+ " 'schemaRulesNotSatisfied': ["
+ " {'operatorName': 'properties',"
+ " 'propertiesNotSatisfied': ["
+ " {'propertyName': 'a', details: ["
+ " {'operatorName': 'encrypt',"
+ " 'reason': 'value was not encrypted'}]}]}]}");
+ doc_validation_error::verifyGeneratedError(query, document, expectedError);
+}
+
+TEST(JSONSchemaValidation, EncryptWithSubtypeFailsBecauseNotEncrypted) {
+ BSONObj query = fromjson(
+ "{'$jsonSchema':"
+ "{bsonType: 'object', properties: {a: {encrypt: {bsonType: 'number'}}}}}");
+ BSONObj document = BSON("a"
+ << "foo");
+ BSONObj expectedError = fromjson(
+ "{'operatorName': '$jsonSchema',"
+ " 'schemaRulesNotSatisfied': ["
+ " {'operatorName': 'properties',"
+ " 'propertiesNotSatisfied': ["
+ " {'propertyName': 'a', details: ["
+ " {'operatorName': 'encrypt',"
+ " 'reason': 'value was not encrypted'}]}]}]}");
+ doc_validation_error::verifyGeneratedError(query, document, expectedError);
+}
+
+TEST(JSONSchemaValidation, EncryptWithSubtypeFailsDueToMismatchedSubtype) {
+ BSONObj query = fromjson(
+ "{'$jsonSchema':"
+ "{bsonType: 'object', properties: {a: {encrypt: {bsonType: 'number'}}}}}");
+ FleBlobHeader blob;
+ blob.fleBlobSubtype = FleBlobSubtype::Deterministic;
+ memset(blob.keyUUID, 0, sizeof(blob.keyUUID));
+ blob.originalBsonType = BSONType::String;
+
+ BSONObj document = BSON("a" << BSONBinData(reinterpret_cast<const void*>(&blob),
+ sizeof(FleBlobHeader),
+ BinDataType::Encrypt));
+
+ BSONObj expectedError = fromjson(
+ "{'operatorName': '$jsonSchema',"
+ " 'schemaRulesNotSatisfied': ["
+ " {'operatorName': 'properties',"
+ " 'propertiesNotSatisfied': ["
+ " {'propertyName': 'a', details: ["
+ " {'operatorName': 'encrypt',"
+ " 'reason': 'encrypted value has wrong type'}]}]}]}");
+ doc_validation_error::verifyGeneratedError(query, document, expectedError);
+}
+
} // namespace
} // namespace mongo
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_fmod.cpp b/src/mongo/db/matcher/schema/expression_internal_schema_fmod.cpp
index 6362c3e8d41..e86e4d0240d 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_fmod.cpp
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_fmod.cpp
@@ -37,10 +37,12 @@
namespace mongo {
-InternalSchemaFmodMatchExpression::InternalSchemaFmodMatchExpression(StringData path,
- Decimal128 divisor,
- Decimal128 remainder)
- : LeafMatchExpression(MatchType::INTERNAL_SCHEMA_FMOD, path),
+InternalSchemaFmodMatchExpression::InternalSchemaFmodMatchExpression(
+ StringData path,
+ Decimal128 divisor,
+ Decimal128 remainder,
+ clonable_ptr<ErrorAnnotation> annotation)
+ : LeafMatchExpression(MatchType::INTERNAL_SCHEMA_FMOD, path, std::move(annotation)),
_divisor(divisor),
_remainder(remainder) {
uassert(ErrorCodes::BadValue, "divisor cannot be 0", !divisor.isZero());
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_fmod.h b/src/mongo/db/matcher/schema/expression_internal_schema_fmod.h
index fd16fa9e220..ae147f3a4fe 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_fmod.h
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_fmod.h
@@ -39,11 +39,15 @@ namespace mongo {
*/
class InternalSchemaFmodMatchExpression final : public LeafMatchExpression {
public:
- InternalSchemaFmodMatchExpression(StringData path, Decimal128 divisor, Decimal128 remainder);
+ InternalSchemaFmodMatchExpression(StringData path,
+ Decimal128 divisor,
+ Decimal128 remainder,
+ clonable_ptr<ErrorAnnotation> annotation = nullptr);
std::unique_ptr<MatchExpression> shallowClone() const final {
std::unique_ptr<InternalSchemaFmodMatchExpression> m =
- std::make_unique<InternalSchemaFmodMatchExpression>(path(), _divisor, _remainder);
+ std::make_unique<InternalSchemaFmodMatchExpression>(
+ path(), _divisor, _remainder, _errorAnnotation);
if (getTag()) {
m->setTag(getTag()->clone());
}
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_max_items.h b/src/mongo/db/matcher/schema/expression_internal_schema_max_items.h
index 4077b43f5f3..ce8cf2a2931 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_max_items.h
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_max_items.h
@@ -40,9 +40,14 @@ namespace mongo {
class InternalSchemaMaxItemsMatchExpression final
: public InternalSchemaNumArrayItemsMatchExpression {
public:
- InternalSchemaMaxItemsMatchExpression(StringData path, long long numItems)
- : InternalSchemaNumArrayItemsMatchExpression(
- INTERNAL_SCHEMA_MAX_ITEMS, path, numItems, "$_internalSchemaMaxItems"_sd) {}
+ InternalSchemaMaxItemsMatchExpression(StringData path,
+ long long numItems,
+ clonable_ptr<ErrorAnnotation> annotation = nullptr)
+ : InternalSchemaNumArrayItemsMatchExpression(INTERNAL_SCHEMA_MAX_ITEMS,
+ path,
+ numItems,
+ "$_internalSchemaMaxItems"_sd,
+ std::move(annotation)) {}
bool matchesArray(const BSONObj& anArray, MatchDetails* details) const final {
return (anArray.nFields() <= numItems());
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_max_length.h b/src/mongo/db/matcher/schema/expression_internal_schema_max_length.h
index eb26a28f897..abe98806e26 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_max_length.h
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_max_length.h
@@ -37,10 +37,14 @@ namespace mongo {
class InternalSchemaMaxLengthMatchExpression final : public InternalSchemaStrLengthMatchExpression {
public:
- InternalSchemaMaxLengthMatchExpression(StringData path, long long strLen)
- : InternalSchemaStrLengthMatchExpression(
- MatchType::INTERNAL_SCHEMA_MAX_LENGTH, path, strLen, "$_internalSchemaMaxLength"_sd) {
- }
+ InternalSchemaMaxLengthMatchExpression(StringData path,
+ long long strLen,
+ clonable_ptr<ErrorAnnotation> annotation = nullptr)
+ : InternalSchemaStrLengthMatchExpression(MatchType::INTERNAL_SCHEMA_MAX_LENGTH,
+ path,
+ strLen,
+ "$_internalSchemaMaxLength"_sd,
+ std::move(annotation)) {}
Validator getComparator() const final {
return [strLen = strLen()](int lenWithoutNullTerm) { return lenWithoutNullTerm <= strLen; };
@@ -48,7 +52,8 @@ public:
std::unique_ptr<MatchExpression> shallowClone() const final {
std::unique_ptr<InternalSchemaMaxLengthMatchExpression> maxLen =
- std::make_unique<InternalSchemaMaxLengthMatchExpression>(path(), strLen());
+ std::make_unique<InternalSchemaMaxLengthMatchExpression>(
+ path(), strLen(), _errorAnnotation);
if (getTag()) {
maxLen->setTag(getTag()->clone());
}
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_min_items.h b/src/mongo/db/matcher/schema/expression_internal_schema_min_items.h
index 1e631ce59f0..50d4768458b 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_min_items.h
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_min_items.h
@@ -40,9 +40,14 @@ namespace mongo {
class InternalSchemaMinItemsMatchExpression final
: public InternalSchemaNumArrayItemsMatchExpression {
public:
- InternalSchemaMinItemsMatchExpression(StringData path, long long numItems)
- : InternalSchemaNumArrayItemsMatchExpression(
- INTERNAL_SCHEMA_MIN_ITEMS, path, numItems, "$_internalSchemaMinItems"_sd) {}
+ InternalSchemaMinItemsMatchExpression(StringData path,
+ long long numItems,
+ clonable_ptr<ErrorAnnotation> annotation = nullptr)
+ : InternalSchemaNumArrayItemsMatchExpression(INTERNAL_SCHEMA_MIN_ITEMS,
+ path,
+ numItems,
+ "$_internalSchemaMinItems"_sd,
+ std::move(annotation)) {}
bool matchesArray(const BSONObj& anArray, MatchDetails* details) const final {
return (anArray.nFields() >= numItems());
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_min_length.h b/src/mongo/db/matcher/schema/expression_internal_schema_min_length.h
index eb26bac606b..d7c4b8aa06b 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_min_length.h
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_min_length.h
@@ -37,10 +37,14 @@ namespace mongo {
class InternalSchemaMinLengthMatchExpression final : public InternalSchemaStrLengthMatchExpression {
public:
- InternalSchemaMinLengthMatchExpression(StringData path, long long strLen)
- : InternalSchemaStrLengthMatchExpression(
- MatchType::INTERNAL_SCHEMA_MIN_LENGTH, path, strLen, "$_internalSchemaMinLength"_sd) {
- }
+ InternalSchemaMinLengthMatchExpression(StringData path,
+ long long strLen,
+ clonable_ptr<ErrorAnnotation> annotation = nullptr)
+ : InternalSchemaStrLengthMatchExpression(MatchType::INTERNAL_SCHEMA_MIN_LENGTH,
+ path,
+ strLen,
+ "$_internalSchemaMinLength"_sd,
+ std::move(annotation)) {}
Validator getComparator() const final {
return [strLen = strLen()](int lenWithoutNullTerm) { return lenWithoutNullTerm >= strLen; };
@@ -48,7 +52,8 @@ public:
std::unique_ptr<MatchExpression> shallowClone() const final {
std::unique_ptr<InternalSchemaMinLengthMatchExpression> minLen =
- std::make_unique<InternalSchemaMinLengthMatchExpression>(path(), strLen());
+ std::make_unique<InternalSchemaMinLengthMatchExpression>(
+ path(), strLen(), _errorAnnotation);
if (getTag()) {
minLen->setTag(getTag()->clone());
}
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_num_array_items.cpp b/src/mongo/db/matcher/schema/expression_internal_schema_num_array_items.cpp
index c6aa1cf0cca..6fde9a327cf 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_num_array_items.cpp
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_num_array_items.cpp
@@ -34,8 +34,14 @@
namespace mongo {
InternalSchemaNumArrayItemsMatchExpression::InternalSchemaNumArrayItemsMatchExpression(
- MatchType type, StringData path, long long numItems, StringData name)
- : ArrayMatchingMatchExpression(type, path), _name(name), _numItems(numItems) {}
+ MatchType type,
+ StringData path,
+ long long numItems,
+ StringData name,
+ clonable_ptr<ErrorAnnotation> annotation)
+ : ArrayMatchingMatchExpression(type, path, std::move(std::move(annotation))),
+ _name(name),
+ _numItems(numItems) {}
void InternalSchemaNumArrayItemsMatchExpression::debugString(StringBuilder& debug,
int indentationLevel) const {
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_num_array_items.h b/src/mongo/db/matcher/schema/expression_internal_schema_num_array_items.h
index ecc269641a0..ca6391bc821 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_num_array_items.h
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_num_array_items.h
@@ -44,7 +44,8 @@ public:
InternalSchemaNumArrayItemsMatchExpression(MatchType type,
StringData path,
long long numItems,
- StringData name);
+ StringData name,
+ clonable_ptr<ErrorAnnotation> annotation = nullptr);
virtual ~InternalSchemaNumArrayItemsMatchExpression() {}
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_str_length.cpp b/src/mongo/db/matcher/schema/expression_internal_schema_str_length.cpp
index d8f961ce35b..87d6396fd0f 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_str_length.cpp
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_str_length.cpp
@@ -35,11 +35,13 @@
namespace mongo {
-InternalSchemaStrLengthMatchExpression::InternalSchemaStrLengthMatchExpression(MatchType type,
- StringData path,
- long long strLen,
- StringData name)
- : LeafMatchExpression(type, path), _name(name), _strLen(strLen) {}
+InternalSchemaStrLengthMatchExpression::InternalSchemaStrLengthMatchExpression(
+ MatchType type,
+ StringData path,
+ long long strLen,
+ StringData name,
+ clonable_ptr<ErrorAnnotation> annotation)
+ : LeafMatchExpression(type, path, std::move(annotation)), _name(name), _strLen(strLen) {}
void InternalSchemaStrLengthMatchExpression::debugString(StringBuilder& debug,
int indentationLevel) const {
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_str_length.h b/src/mongo/db/matcher/schema/expression_internal_schema_str_length.h
index 8721a8d718a..43483be1ce3 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_str_length.h
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_str_length.h
@@ -43,7 +43,8 @@ public:
InternalSchemaStrLengthMatchExpression(MatchType type,
StringData path,
long long strLen,
- StringData name);
+ StringData name,
+ clonable_ptr<ErrorAnnotation> annotation = nullptr);
virtual ~InternalSchemaStrLengthMatchExpression() {}
diff --git a/src/mongo/db/matcher/schema/json_schema_parser.cpp b/src/mongo/db/matcher/schema/json_schema_parser.cpp
index 9e11324c732..7de01f1ee43 100644
--- a/src/mongo/db/matcher/schema/json_schema_parser.cpp
+++ b/src/mongo/db/matcher/schema/json_schema_parser.cpp
@@ -287,11 +287,13 @@ StatusWithMatchExpression parseLength(const boost::intrusive_ptr<ExpressionConte
return parsedLength.getStatus();
}
+ auto annotation = doc_validation_error::createAnnotation(
+ expCtx, length.fieldNameStringData().toString(), length.wrap());
if (path.empty()) {
- return {std::make_unique<AlwaysTrueMatchExpression>()};
+ return {std::make_unique<AlwaysTrueMatchExpression>(std::move(annotation))};
}
- auto expr = std::make_unique<T>(path, parsedLength.getValue());
+ auto expr = std::make_unique<T>(path, parsedLength.getValue(), std::move(annotation));
return makeRestriction(expCtx, restrictionType, path, std::move(expr), typeExpr);
}
@@ -306,13 +308,16 @@ StatusWithMatchExpression parsePattern(const boost::intrusive_ptr<ExpressionCont
<< "' must be a string")};
}
+ auto annotation = doc_validation_error::createAnnotation(
+ expCtx, pattern.fieldNameStringData().toString(), pattern.wrap());
if (path.empty()) {
- return {std::make_unique<AlwaysTrueMatchExpression>()};
+ return {std::make_unique<AlwaysTrueMatchExpression>(std::move(annotation))};
}
// JSON Schema does not allow regex flags to be specified.
constexpr auto emptyFlags = "";
- auto expr = std::make_unique<RegexMatchExpression>(path, pattern.valueStringData(), emptyFlags);
+ auto expr = std::make_unique<RegexMatchExpression>(
+ path, pattern.valueStringData(), emptyFlags, std::move(annotation));
return makeRestriction(expCtx, BSONType::String, path, std::move(expr), typeExpr);
}
@@ -334,12 +339,14 @@ StatusWithMatchExpression parseMultipleOf(const boost::intrusive_ptr<ExpressionC
<< "$jsonSchema keyword '" << JSONSchemaParser::kSchemaMultipleOfKeyword
<< "' must have a positive value")};
}
+ auto annotation = doc_validation_error::createAnnotation(
+ expCtx, multipleOf.fieldNameStringData().toString(), multipleOf.wrap());
if (path.empty()) {
- return {std::make_unique<AlwaysTrueMatchExpression>()};
+ return {std::make_unique<AlwaysTrueMatchExpression>(std::move(annotation))};
}
auto expr = std::make_unique<InternalSchemaFmodMatchExpression>(
- path, multipleOf.numberDecimal(), Decimal128(0));
+ path, multipleOf.numberDecimal(), Decimal128(0), std::move(annotation));
MatcherTypeSet restrictionType;
restrictionType.allNumbers = true;
@@ -1465,11 +1472,19 @@ Status translateEncryptionKeywords(StringMap<BSONElement>& keywordMap,
auto encryptInfo = EncryptionInfo::parse(encryptCtxt, encryptElt.embeddedObject());
auto infoType = encryptInfo.getBsonType();
- andExpr->add(new InternalSchemaBinDataSubTypeExpression(path, BinDataType::Encrypt));
+ andExpr->add(new InternalSchemaBinDataSubTypeExpression(
+ path,
+ BinDataType::Encrypt,
+ doc_validation_error::createAnnotation(
+ expCtx, encryptElt.fieldNameStringData().toString(), BSONObj())));
- if (auto typeOptional = infoType)
+ if (auto typeOptional = infoType) {
andExpr->add(new InternalSchemaBinDataEncryptedTypeExpression(
- path, typeOptional->typeSet()));
+ path,
+ typeOptional->typeSet(),
+ doc_validation_error::createAnnotation(
+ expCtx, encryptElt.fieldNameStringData().toString(), BSONObj())));
+ }
} catch (const AssertionException&) {
return exceptionToStatus();
}
@@ -1624,8 +1639,10 @@ StatusWithMatchExpression _parse(const boost::intrusive_ptr<ExpressionContext>&
} else if (encryptElem) {
// The presence of the encrypt keyword implies the restriction that the field must be
// of type BinData.
- typeExpr =
- std::make_unique<InternalSchemaTypeExpression>(path, MatcherTypeSet(BSONType::BinData));
+ typeExpr = std::make_unique<InternalSchemaTypeExpression>(
+ path,
+ MatcherTypeSet(BSONType::BinData),
+ doc_validation_error::createAnnotation(expCtx, AnnotationMode::kIgnore));
}
auto andExpr = std::make_unique<AndMatchExpression>(