diff options
author | Mark Benvenuto <mark.benvenuto@mongodb.com> | 2021-06-22 21:56:46 -0400 |
---|---|---|
committer | Mark Benvenuto <mark.benvenuto@mongodb.com> | 2021-06-23 16:48:14 -0400 |
commit | 651361eec8e4ee468c33e21dd6f346bf141868b3 (patch) | |
tree | 61dda2e7f8d1bcbb3f26676130ad463a6c0b9d40 | |
parent | f9bed91448c7a6f1bd1681365f09fd0767efb21f (diff) | |
download | mongo-651361eec8e4ee468c33e21dd6f346bf141868b3.tar.gz |
SERVER-57540 IDL accepts null CommitQuorum type
(cherry picked from commit 3be5fb13dfd534fbb91cb93a45c350d7dd58d70c)
-rw-r--r-- | buildscripts/idl/idl/generator.py | 122 | ||||
-rw-r--r-- | src/mongo/idl/idl_test.cpp | 7 |
2 files changed, 62 insertions, 67 deletions
diff --git a/buildscripts/idl/idl/generator.py b/buildscripts/idl/idl/generator.py index 83ce7b28ba2..825f89e5db5 100644 --- a/buildscripts/idl/idl/generator.py +++ b/buildscripts/idl/idl/generator.py @@ -208,57 +208,6 @@ class _FieldUsageCheckerBase(object, metaclass=ABCMeta): pass -class _SlowFieldUsageChecker(_FieldUsageCheckerBase): - """ - Check for duplicate fields, and required fields as needed. - - Detects duplicate extra fields. - Generates code with a C++ std::set to maintain a set of fields seen while parsing a BSON - document. The std::set has O(N lg N) lookup, and allocates memory in the heap. - """ - - def __init__(self, indented_writer): - # type: (writer.IndentedTextWriter) -> None - super(_SlowFieldUsageChecker, self).__init__(indented_writer) - - self._writer.write_line('std::set<StringData> usedFields;') - - def add_store(self, field_name): - # type: (str) -> None - self._writer.write_line('auto push_result = usedFields.insert(%s);' % (field_name)) - with writer.IndentedScopedBlock(self._writer, - 'if (MONGO_unlikely(push_result.second == false)) {', '}'): - self._writer.write_line('ctxt.throwDuplicateField(%s);' % (field_name)) - - def add(self, field, bson_element_variable): - # type: (ast.Field, str) -> None - if not field in self._fields: - self._fields.append(field) - - def add_final_checks(self): - # type: () -> None - for field in self._fields: - if (not field.optional) and (not field.ignore) and (not field.chained): - pred = 'if (MONGO_unlikely(usedFields.find(%s) == usedFields.end())) {' % \ - (_get_field_constant_name(field)) - with writer.IndentedScopedBlock(self._writer, pred, '}'): - if field.default: - if field.chained_struct_field: - self._writer.write_line('%s.%s(%s);' % (_get_field_member_name( - field.chained_struct_field), _get_field_member_setter_name(field), - field.default)) - elif field.type.is_enum: - self._writer.write_line( - '%s = %s::%s;' % (_get_field_member_name(field), - field.type.cpp_type, field.default)) - else: - self._writer.write_line( - '%s = %s;' % (_get_field_member_name(field), field.default)) - else: - self._writer.write_line( - 'ctxt.throwMissingField(%s);' % (_get_field_constant_name(field))) - - def _gen_field_usage_constant(field): # type: (ast.Field) -> str """Get the name for a bitset constant in field usage checking.""" @@ -331,23 +280,40 @@ class _FastFieldUsageChecker(_FieldUsageCheckerBase): self._writer, 'if (!usedFields[%s]) {' % (_gen_field_usage_constant(field)), '}'): if field.default: + default_value = (field.type.cpp_type + "::" + field.default) \ + if field.type.is_enum else field.default if field.chained_struct_field: self._writer.write_line( '%s.%s(%s);' % (_get_field_member_name(field.chained_struct_field), - _get_field_member_setter_name(field), field.default)) - elif field.type.is_enum: - self._writer.write_line( - '%s = %s::%s;' % (_get_field_member_name(field), - field.type.cpp_type, field.default)) + _get_field_member_setter_name(field), default_value)) else: self._writer.write_line( - '%s = %s;' % (_get_field_member_name(field), field.default)) + '%s = %s;' % (_get_field_member_name(field), default_value)) else: self._writer.write_line( 'ctxt.throwMissingField(%s);' % (_get_field_constant_name(field))) +class _SlowFieldUsageChecker(_FastFieldUsageChecker): + """ + Check for duplicate fields, and required fields as needed. + + Generates code with a C++ std::set to maintain a set of fields seen while parsing a BSON + document. The std::set has O(N lg N) lookup, and allocates memory in the heap. + The fast and slow duplicate/field usage checkers are merged together through + inheritance. The fast checker assumes it only needs to check a finite list of + fields for duplicates. The slow checker simply uses the fast check for all known + fields and a std::set for other fields to detect duplication. + """ + + def __init__(self, indented_writer, fields): + # type: (writer.IndentedTextWriter, List[ast.Field]) -> None + super(_SlowFieldUsageChecker, self).__init__(indented_writer, fields) + + self._writer.write_line('std::set<StringData> usedFieldSet;') + + def _get_field_usage_checker(indented_writer, struct): # type: (writer.IndentedTextWriter, ast.Struct) -> _FieldUsageCheckerBase @@ -356,7 +322,7 @@ def _get_field_usage_checker(indented_writer, struct): if struct.strict: return _FastFieldUsageChecker(indented_writer, struct.fields) - return _SlowFieldUsageChecker(indented_writer) + return _SlowFieldUsageChecker(indented_writer, struct.fields) # Turn a python string into a C++ literal. @@ -493,6 +459,14 @@ class _CppFileWriterBase(object): return writer.IndentedScopedBlock(self._writer, '%s (%s) {' % (conditional, check_str), '}') + def _else(self, check_bool): + # type: (bool) -> Union[writer.IndentedScopedBlock,writer.EmptyBlock] + """Generate an else block if check_bool is true.""" + if not check_bool: + return writer.EmptyBlock() + + return writer.IndentedScopedBlock(self._writer, 'else {', '}') + def _condition(self, condition, preprocessor_only=False): # type: (ast.Condition, bool) -> writer.WriterBlock """Generate one or more blocks for multiple conditional types.""" @@ -1701,7 +1675,6 @@ class _CppSourceFileWriter(_CppFileWriterBase): self._writer.write_line('firstFieldFound = true;') self._writer.write_line('continue;') - field_usage_check.add_store("fieldName") self._writer.write_empty_line() first_field = True @@ -1728,15 +1701,22 @@ class _CppSourceFileWriter(_CppFileWriterBase): # End of for fields # Generate strict check for extranous fields if struct.strict: - with self._block('else {', '}'): - # For commands, check if this a well known command field that the IDL parser - # should ignore regardless of strict mode. - command_predicate = None - if isinstance(struct, ast.Command): - command_predicate = "!mongo::isGenericArgument(fieldName)" + # For commands, check if this is a well known command field that the IDL parser + # should ignore regardless of strict mode. + command_predicate = None + if isinstance(struct, ast.Command): + command_predicate = "!mongo::isGenericArgument(fieldName)" + with self._block('else {', '}'): with self._predicate(command_predicate): self._writer.write_line('ctxt.throwUnknownField(fieldName);') + else: + with self._else(not first_field): + self._writer.write_line('auto push_result = usedFieldSet.insert(fieldName);') + with writer.IndentedScopedBlock( + self._writer, 'if (MONGO_unlikely(push_result.second == false)) {', + '}'): + self._writer.write_line('ctxt.throwDuplicateField(fieldName);') # Parse chained structs if not inlined # Parse chained types always here @@ -1921,9 +1901,17 @@ class _CppSourceFileWriter(_CppFileWriterBase): # End of for fields # Generate strict check for extranous fields - if struct.strict: - with self._block('else {', '}'): + with self._block('else {', '}'): + if struct.strict: self._writer.write_line('ctxt.throwUnknownField(sequence.name);') + else: + self._writer.write_line( + 'auto push_result = usedFieldSet.insert(sequence.name);') + with writer.IndentedScopedBlock( + self._writer, + 'if (MONGO_unlikely(push_result.second == false)) {', '}'): + self._writer.write_line('ctxt.throwDuplicateField(sequence.name);') + self._writer.write_empty_line() # Check for required fields diff --git a/src/mongo/idl/idl_test.cpp b/src/mongo/idl/idl_test.cpp index e2f8cb030ea..17267f0cfde 100644 --- a/src/mongo/idl/idl_test.cpp +++ b/src/mongo/idl/idl_test.cpp @@ -1023,6 +1023,13 @@ TEST(IDLStructTests, TestNonStrictStruct) { BSON("field4" << 1234 << "1" << 12 << "2" << 123 << "3" << 1234 << "field4" << 1234); ASSERT_THROWS(RequiredNonStrictField3::parse(ctxt, testDoc), AssertionException); } + + // Negative: null required field + { + auto testDoc = BSON(RequiredNonStrictField3::kCppField1FieldName << 12 << "2" << 123 << "3" + << BSONNULL); + ASSERT_THROWS(RequiredNonStrictField3::parse(ctxt, testDoc), AssertionException); + } } TEST(IDLStructTests, WriteConcernTest) { |