summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorA. Jesse Jiryu Davis <jesse@mongodb.com>2020-11-10 12:43:17 -0500
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-11-11 01:31:47 +0000
commitc4f0c53602d42366c9c16b0f92482a35440c1828 (patch)
tree093888f11c067a309921290b6a318f2bc646bcae
parent523247d096a796c15c911370e622a3614411a25b (diff)
downloadmongo-c4f0c53602d42366c9c16b0f92482a35440c1828.tar.gz
SERVER-51848 Specific generic args in IDL
-rw-r--r--buildscripts/idl/idl/ast.py69
-rw-r--r--buildscripts/idl/idl/binder.py55
-rw-r--r--buildscripts/idl/idl/generator.py107
-rw-r--r--buildscripts/idl/idl/generic_field_list_types.py64
-rw-r--r--buildscripts/idl/idl/parser.py105
-rw-r--r--buildscripts/idl/idl/syntax.py51
-rw-r--r--buildscripts/idl/tests/test_parser.py124
-rw-r--r--src/mongo/bson/util/bson_check.h2
-rw-r--r--src/mongo/db/SConscript13
-rw-r--r--src/mongo/db/auth/user_management_commands_parser.cpp2
-rw-r--r--src/mongo/db/catalog/SConscript2
-rw-r--r--src/mongo/db/catalog/coll_mod.cpp2
-rw-r--r--src/mongo/db/catalog/collection_options.cpp2
-rw-r--r--src/mongo/db/catalog/create_collection.cpp2
-rw-r--r--src/mongo/db/commands.cpp2
-rw-r--r--src/mongo/db/commands/SConscript1
-rw-r--r--src/mongo/db/commands/create_indexes.cpp2
-rw-r--r--src/mongo/db/commands/current_op_common.cpp2
-rw-r--r--src/mongo/db/commands/dbcheck.cpp2
-rw-r--r--src/mongo/db/commands/get_last_error.cpp2
-rw-r--r--src/mongo/db/commands/parameters.cpp2
-rw-r--r--src/mongo/db/commands_bm.cpp2
-rw-r--r--src/mongo/db/pipeline/aggregation_request.cpp2
-rw-r--r--src/mongo/db/query/SConscript2
-rw-r--r--src/mongo/db/query/explain_options.cpp2
-rw-r--r--src/mongo/db/query/find_and_modify_request.cpp2
-rw-r--r--src/mongo/db/query/getmore_request.cpp2
-rw-r--r--src/mongo/db/query/query_request.cpp2
-rw-r--r--src/mongo/idl/SConscript5
-rw-r--r--src/mongo/idl/command_generic_argument.cpp56
-rw-r--r--src/mongo/idl/command_generic_argument.h (renamed from src/mongo/db/command_generic_argument.h)0
-rw-r--r--src/mongo/idl/command_generic_argument_test.cpp (renamed from src/mongo/db/command_generic_argument.cpp)62
-rw-r--r--src/mongo/idl/generic_argument.idl130
-rw-r--r--src/mongo/idl/idl_parser.cpp2
-rw-r--r--src/mongo/idl/unittest.idl27
-rw-r--r--src/mongo/s/commands/cluster_explain.cpp2
-rw-r--r--src/mongo/s/commands/cluster_explain_cmd.cpp2
37 files changed, 808 insertions, 105 deletions
diff --git a/buildscripts/idl/idl/ast.py b/buildscripts/idl/idl/ast.py
index 033b0df002c..e5d43e77067 100644
--- a/buildscripts/idl/idl/ast.py
+++ b/buildscripts/idl/idl/ast.py
@@ -33,11 +33,10 @@ Represents the derived IDL specification after type resolution in the binding pa
This is a lossy translation from the IDL Syntax tree as the IDL AST only contains information about
the enums and structs that need code generated for them, and just enough information to do that.
"""
+from abc import ABCMeta, abstractmethod
+from typing import List, Optional
-from typing import List, Union, Any, Optional, Tuple
-
-from . import common
-from . import errors
+from . import common, errors
class IDLBoundSpec(object):
@@ -55,6 +54,8 @@ class IDLBoundSpec(object):
class IDLAST(object):
"""The in-memory representation of an IDL file."""
+ # pylint: disable=too-many-instance-attributes
+
def __init__(self):
# type: () -> None
"""Construct an IDLAST."""
@@ -63,7 +64,8 @@ class IDLAST(object):
self.commands = [] # type: List[Command]
self.enums = [] # type: List[Enum]
self.structs = [] # type: List[Struct]
-
+ self.generic_argument_lists = [] # type: List[GenericArgumentList]
+ self.generic_reply_field_lists = [] # type: List[GenericReplyFieldList]
self.server_parameters = [] # type: List[ServerParameter]
self.configs = [] # type: List[ConfigOption]
@@ -217,10 +219,65 @@ class Command(Struct):
self.api_version = "" # type: str
self.is_deprecated = False # type: bool
self.unstable = False # type: bool
+ super(Command, self).__init__(file_name, line, column)
+
+
+class FieldListBase(common.SourceLocation, metaclass=ABCMeta):
+ """Base class for generic argument or reply field lists."""
+
+ def __init__(self, file_name, line, column):
+ # type: (str, int, int) -> None
+ """Construct a field list."""
+ self.name = None # type: str
+ self.cpp_name = None # type: str
+ self.description = None # type: str
+ self.fields = [] # type: List[FieldListEntry]
+ super(FieldListBase, self).__init__(file_name, line, column)
+
+ @abstractmethod
+ def get_should_forward_name(self):
+ # type: () -> str
+ """Get the name of the shard-forwarding rule for this generic argument or reply field."""
+ pass
+
+
+class GenericArgumentList(FieldListBase):
+ """IDL generic argument list."""
+
+ def get_should_forward_name(self):
+ # type: () -> str
+ """Get the name of the shard-forwarding rule for a generic argument."""
+ return "shouldForwardToShards"
+
+
+class GenericReplyFieldList(FieldListBase):
+ """IDL generic reply field list."""
+
+ def get_should_forward_name(self):
+ # type: () -> str
+ """Get the name of the shard-forwarding rule for a generic reply field."""
+ return "shouldForwardFromShards"
+
+
+class FieldListEntry(common.SourceLocation):
+ """Options for a field in a field list."""
+
+ def __init__(self, file_name, line, column):
+ # type: (str, int, int) -> None
+ """Construct a FieldListEntry."""
+ self.name = None # type: str
self.forward_to_shards = False # type: bool
self.forward_from_shards = False # type: bool
+
self.command_name = None # type: str
- super(Command, self).__init__(file_name, line, column)
+ super(FieldListEntry, self).__init__(file_name, line, column)
+
+ def get_should_forward(self):
+ """Get the shard-forwarding rule for a generic argument or reply field."""
+ assert not (self.forward_to_shards and self.forward_from_shards), \
+ "Only FieldListEntry.forward_to_shards or forward_from_shards should be set"
+
+ return self.forward_to_shards or self.forward_from_shards
class EnumValue(common.SourceLocation):
diff --git a/buildscripts/idl/idl/binder.py b/buildscripts/idl/idl/binder.py
index 397c39533cb..e65b7f64dfb 100644
--- a/buildscripts/idl/idl/binder.py
+++ b/buildscripts/idl/idl/binder.py
@@ -29,12 +29,11 @@
"""Transform idl.syntax trees from the parser into well-defined idl.ast trees."""
import re
-from typing import cast, List, Set, Union
+from typing import Type, TypeVar, cast, List, Set, Union
from . import ast
from . import bson
from . import common
-from . import cpp_types
from . import enum_types
from . import errors
from . import syntax
@@ -334,6 +333,52 @@ def _bind_struct(ctxt, parsed_spec, struct):
return ast_struct
+def _bind_field_list_entry(field_list_entry):
+ # type: (syntax.FieldListEntry) -> ast.FieldListEntry
+ """Bind a generic argument or reply field list entry."""
+ ast_entry = ast.FieldListEntry(field_list_entry.file_name, field_list_entry.line,
+ field_list_entry.column)
+ ast_entry.name = field_list_entry.name
+ ast_entry.forward_to_shards = field_list_entry.forward_to_shards
+ ast_entry.forward_from_shards = field_list_entry.forward_from_shards
+ return ast_entry
+
+
+ASTFieldListBaseClass = TypeVar("ASTFieldListBaseClass", bound=ast.FieldListBase, covariant=True)
+
+
+def _bind_field_list(field_list, ast_class):
+ # type: (syntax.FieldListBase, Type[ASTFieldListBaseClass]) -> ASTFieldListBaseClass
+ """Bind a generic argument or reply field list (helper method).
+
+ The ast_class param must be a subclass of ast.FieldListBase. The returned value is an
+ instance of ast_class.
+ """
+ ast_field_list = ast_class(field_list.file_name, field_list.line, field_list.column)
+ ast_field_list.description = field_list.description
+ ast_field_list.cpp_name = field_list.name
+ if field_list.cpp_name:
+ ast_field_list.cpp_name = field_list.cpp_name
+
+ return ast_field_list
+
+
+def _bind_generic_argument_list(field_list):
+ # type: (syntax.GenericArgumentList) -> ast.GenericArgumentList
+ """Bind a generic argument list."""
+ ast_field_list = _bind_field_list(field_list, ast.GenericArgumentList)
+ ast_field_list.fields = [_bind_field_list_entry(f) for f in field_list.fields]
+ return ast_field_list
+
+
+def _bind_generic_reply_field_list(field_list):
+ # type: (syntax.GenericReplyFieldList) -> ast.GenericReplyFieldList
+ """Bind a generic reply field list."""
+ ast_field_list = _bind_field_list(field_list, ast.GenericReplyFieldList)
+ ast_field_list.fields = [_bind_field_list_entry(f) for f in field_list.fields]
+ return ast_field_list
+
+
def _inject_hidden_command_fields(command):
# type: (syntax.Command) -> None
"""Inject hidden fields to aid deserialization/serialization for OpMsg parsing of commands."""
@@ -1223,6 +1268,12 @@ def bind(parsed_spec):
if not struct.imported:
bound_spec.structs.append(_bind_struct(ctxt, parsed_spec, struct))
+ for arg_list in parsed_spec.symbols.generic_argument_lists:
+ bound_spec.generic_argument_lists.append(_bind_generic_argument_list(arg_list))
+
+ for field_list in parsed_spec.symbols.generic_reply_field_lists:
+ bound_spec.generic_reply_field_lists.append(_bind_generic_reply_field_list(field_list))
+
for feature_flag in parsed_spec.feature_flags:
bound_spec.server_parameters.append(_bind_feature_flags(ctxt, feature_flag))
diff --git a/buildscripts/idl/idl/generator.py b/buildscripts/idl/idl/generator.py
index 70eb1e00499..295fabb2f44 100644
--- a/buildscripts/idl/idl/generator.py
+++ b/buildscripts/idl/idl/generator.py
@@ -28,24 +28,17 @@
# pylint: disable=too-many-lines
"""IDL C++ Code Generator."""
-from abc import ABCMeta, abstractmethod
-import copy
+import hashlib
import io
import os
import re
-import string
import sys
import textwrap
-import hashlib
-from typing import cast, Dict, List, Mapping, Tuple, Union
+from abc import ABCMeta, abstractmethod
+from typing import Dict, Iterable, List, Mapping, Tuple, Union
-from . import ast
-from . import bson
-from . import common
-from . import cpp_types
-from . import enum_types
-from . import struct_types
-from . import writer
+from . import (ast, bson, common, cpp_types, enum_types, generic_field_list_types, struct_types,
+ writer)
def _get_field_member_name(field):
@@ -539,6 +532,13 @@ class _CppHeaderFileWriter(_CppFileWriterBase):
if len(required_constructor.args) != len(constructor.args):
self._writer.write_line(required_constructor.get_declaration())
+ def gen_field_list_entry_lookup_methods(self, field_list):
+ # type: (ast.FieldListBase) -> None
+ """Generate the declarations for generic argument or reply field lookup methods."""
+ field_list_info = generic_field_list_types.get_field_list_info(field_list)
+ self._writer.write_line(field_list_info.get_has_field_method().get_declaration())
+ self._writer.write_line(field_list_info.get_should_forward_method().get_declaration())
+
def gen_serializer_methods(self, struct):
# type: (ast.Struct) -> None
"""Generate a serializer method declarations."""
@@ -761,6 +761,17 @@ class _CppHeaderFileWriter(_CppFileWriterBase):
self._writer.write_empty_line()
+ def gen_field_list_entries_declaration(self, field_list):
+ # type: (ast.FieldListBase) -> None
+ """Generate the field list entries map for a generic argument or reply field list."""
+ field_list_info = generic_field_list_types.get_field_list_info(field_list)
+ self._writer.write_line(
+ common.template_args('// Map: fieldName -> ${should_forward_name}',
+ should_forward_name=field_list_info.get_should_forward_name()))
+ self._writer.write_line(
+ "static const stdx::unordered_map<std::string, bool> _genericFields;")
+ self.write_empty_line()
+
def gen_known_fields_declaration(self):
# type: () -> None
"""Generate all the known fields vectors for a command."""
@@ -934,6 +945,7 @@ class _CppHeaderFileWriter(_CppFileWriterBase):
'mongo/bson/simple_bsonobj_comparator.h',
'mongo/idl/idl_parser.h',
'mongo/rpc/op_msg.h',
+ 'mongo/stdx/unordered_map.h',
] + spec.globals.cpp_includes
if spec.configs:
@@ -1036,6 +1048,24 @@ class _CppHeaderFileWriter(_CppFileWriterBase):
self.write_empty_line()
+ field_lists_list: Iterable[Iterable[ast.FieldListBase]]
+ field_lists_list = [spec.generic_argument_lists, spec.generic_reply_field_lists]
+ for field_lists in field_lists_list:
+ for field_list in field_lists:
+ self.gen_description_comment(field_list.description)
+ with self.gen_class_declaration_block(field_list.cpp_name):
+ self.write_unindented_line('public:')
+
+ # Field lookup methods
+ self.gen_field_list_entry_lookup_methods(field_list)
+ self.write_empty_line()
+
+ # Member variables
+ self.write_unindented_line('private:')
+ self.gen_field_list_entries_declaration(field_list)
+
+ self.write_empty_line()
+
for scp in spec.server_parameters:
if scp.cpp_class is None:
self._gen_exported_constexpr(scp.name, 'Default', scp.default, scp.condition)
@@ -1344,6 +1374,24 @@ class _CppSourceFileWriter(_CppFileWriterBase):
#print(struct.name + ": "+ str(required_constructor.args))
self._gen_constructor(struct, required_constructor, False)
+ def gen_field_list_entry_lookup_methods(self, field_list):
+ # type: (ast.FieldListBase) -> None
+ """Generate the definitions for generic argument or reply field lookup methods."""
+ field_list_info = generic_field_list_types.get_field_list_info(field_list)
+ defn = field_list_info.get_has_field_method().get_definition()
+ with self._block('%s {' % (defn, ), '}'):
+ self._writer.write_line(
+ 'return _genericFields.find(fieldName.toString()) != _genericFields.end();')
+
+ self._writer.write_empty_line()
+
+ defn = field_list_info.get_should_forward_method().get_definition()
+ with self._block('%s {' % (defn, ), '}'):
+ self._writer.write_line('auto it = _genericFields.find(fieldName.toString());')
+ self._writer.write_line('return (it == _genericFields.end() || it->second);')
+
+ self._writer.write_empty_line()
+
def _gen_command_deserializer(self, struct, bson_object):
# type: (ast.Struct, str) -> None
"""Generate the command field deserializer."""
@@ -1937,6 +1985,24 @@ class _CppSourceFileWriter(_CppFileWriterBase):
self._gen_known_fields_declaration(struct, "knownBSON", False)
self._gen_known_fields_declaration(struct, "knownOP_MSG", True)
+ def gen_field_list_entries_declaration(self, field_list):
+ # type: (ast.FieldListBase) -> None
+ """Generate the field list entries map for a generic argument or reply field list."""
+ klass = common.title_case(field_list.cpp_name)
+ field_list_info = generic_field_list_types.get_field_list_info(field_list)
+ self._writer.write_line(
+ common.template_args('// Map: fieldName -> ${should_forward_name}',
+ should_forward_name=field_list_info.get_should_forward_name()))
+ block_name = common.template_args(
+ 'const stdx::unordered_map<std::string, bool> ${klass}::_genericFields {', klass=klass)
+ with self._block(block_name, "};"):
+ sorted_entries = sorted(field_list.fields, key=lambda f: f.name)
+ for entry in sorted_entries:
+ self._writer.write_line(
+ common.template_args(
+ '{"${name}", ${should_forward}},', klass=klass, name=entry.name,
+ should_forward='true' if entry.get_should_forward() else 'false'))
+
def _gen_server_parameter_specialized(self, param):
# type: (ast.ServerParameter) -> None
"""Generate a specialized ServerParameter."""
@@ -2250,6 +2316,9 @@ class _CppSourceFileWriter(_CppFileWriterBase):
def generate(self, spec, header_file_name):
# type: (ast.IDLAST, str) -> None
"""Generate the C++ header to a stream."""
+
+ # pylint: disable=too-many-statements
+
self.gen_file_header()
# Include platform/basic.h
@@ -2274,8 +2343,8 @@ class _CppSourceFileWriter(_CppFileWriterBase):
# Generate mongo includes third
header_list = [
'mongo/bson/bsonobjbuilder.h',
- 'mongo/db/command_generic_argument.h',
'mongo/db/commands.h',
+ 'mongo/idl/command_generic_argument.h',
]
if spec.server_parameters:
@@ -2337,6 +2406,18 @@ class _CppSourceFileWriter(_CppFileWriterBase):
self.gen_to_bson_serializer_method(struct)
self.write_empty_line()
+ field_lists_list: Iterable[Iterable[ast.FieldListBase]]
+ field_lists_list = [spec.generic_argument_lists, spec.generic_reply_field_lists]
+ for field_lists in field_lists_list:
+ for field_list in field_lists:
+ # Member variables
+ self.gen_field_list_entries_declaration(field_list)
+ self.write_empty_line()
+
+ # Write field lookup methods
+ self.gen_field_list_entry_lookup_methods(field_list)
+ self.write_empty_line()
+
if spec.server_parameters:
self.gen_server_parameters(spec.server_parameters, header_file_name)
if spec.configs:
diff --git a/buildscripts/idl/idl/generic_field_list_types.py b/buildscripts/idl/idl/generic_field_list_types.py
new file mode 100644
index 00000000000..62cffc77a81
--- /dev/null
+++ b/buildscripts/idl/idl/generic_field_list_types.py
@@ -0,0 +1,64 @@
+# Copyright (C) 2020-present MongoDB, Inc.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the Server Side Public License, version 1,
+# as published by MongoDB, Inc.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# Server Side Public License for more details.
+#
+# You should have received a copy of the Server Side Public License
+# along with this program. If not, see
+# <http://www.mongodb.com/licensing/server-side-public-license>.
+#
+# As a special exception, the copyright holders give permission to link the
+# code of portions of this program with the OpenSSL library under certain
+# conditions as described in each individual source file and distribute
+# linked combinations including the program with the OpenSSL library. You
+# must comply with the Server Side Public License in all respects for
+# all of the code used other than as permitted herein. If you modify file(s)
+# with this exception, you may extend this exception to your version of the
+# file(s), but you are not obligated to do so. If you do not wish to do so,
+# delete this exception statement from your version. If you delete this
+# exception statement from all source files in the program, then also delete
+# it in the license file.
+#
+"""Provide code generation information for generic arguments and reply fields."""
+
+from . import ast, common
+from .struct_types import MethodInfo
+
+
+class FieldListInfo:
+ """Class for generic argument and generic reply field list code generation."""
+
+ def __init__(self, field_list):
+ # type: (ast.FieldListBase) -> None
+ """Create a FieldListInfo instance."""
+ self._field_list = field_list
+
+ def get_has_field_method(self):
+ # type: () -> MethodInfo
+ """Get the hasField method for a generic argument or generic reply field list."""
+ class_name = common.title_case(self._field_list.cpp_name)
+ return MethodInfo(class_name, 'hasField', ['StringData fieldName'], 'bool', static=True)
+
+ def get_should_forward_name(self):
+ """Get the name of the shard-forwarding rule for a generic argument or reply field."""
+ return self._field_list.get_should_forward_name()
+
+ def get_should_forward_method(self):
+ # type: () -> MethodInfo
+ """Get the method for checking the shard-forwarding rule of an argument or reply field."""
+ class_name = common.title_case(self._field_list.cpp_name)
+ return MethodInfo(class_name, self.get_should_forward_name(), ['StringData fieldName'],
+ 'bool', static=True)
+
+
+def get_field_list_info(field_list):
+ # type: (ast.FieldListBase) -> FieldListInfo
+ """Get type information about the generic argument or reply field list to generate C++ code."""
+
+ return FieldListInfo(field_list)
diff --git a/buildscripts/idl/idl/parser.py b/buildscripts/idl/idl/parser.py
index 9378db49531..0ad4b7766df 100644
--- a/buildscripts/idl/idl/parser.py
+++ b/buildscripts/idl/idl/parser.py
@@ -461,6 +461,103 @@ def _parse_struct(ctxt, spec, name, node):
spec.symbols.add_struct(ctxt, struct)
+def _parse_generic_argument_list(ctxt, spec, name, node):
+ # type: (errors.ParserContext, syntax.IDLSpec, str, Union[yaml.nodes.MappingNode, yaml.nodes.ScalarNode, yaml.nodes.SequenceNode]) -> None
+ """Parse a generic_argument_lists section in the IDL file."""
+ if not ctxt.is_mapping_node(node, "generic_argument_list"):
+ return
+
+ field_list = syntax.GenericArgumentList(ctxt.file_name, node.start_mark.line,
+ node.start_mark.column)
+ field_list.name = name
+
+ _generic_parser(
+ ctxt, node, "generic_argument_list", field_list, {
+ "description":
+ _RuleDesc('scalar', _RuleDesc.REQUIRED),
+ "cpp_name":
+ _RuleDesc('scalar'),
+ "fields":
+ _RuleDesc('mapping', mapping_parser_func=_parse_generic_argument_list_entries),
+ })
+
+ spec.symbols.add_generic_argument_list(ctxt, field_list)
+
+
+def _parse_generic_reply_field_list(ctxt, spec, name, node):
+ # type: (errors.ParserContext, syntax.IDLSpec, str, Union[yaml.nodes.MappingNode, yaml.nodes.ScalarNode, yaml.nodes.SequenceNode]) -> None
+ """Parse a generic_reply_field_lists section in the IDL file."""
+ if not ctxt.is_mapping_node(node, "generic_reply_field_list"):
+ return
+
+ field_list = syntax.GenericReplyFieldList(ctxt.file_name, node.start_mark.line,
+ node.start_mark.column)
+ field_list.name = name
+
+ _generic_parser(
+ ctxt, node, "generic_reply_field_list", field_list, {
+ "description":
+ _RuleDesc('scalar', _RuleDesc.REQUIRED),
+ "cpp_name":
+ _RuleDesc('scalar'),
+ "fields":
+ _RuleDesc('mapping', mapping_parser_func=_parse_generic_reply_field_list_entries),
+ })
+
+ spec.symbols.add_generic_reply_field_list(ctxt, field_list)
+
+
+def _parse_field_list_entry(ctxt, name, node, is_generic_argument_field_list):
+ # type: (errors.ParserContext, str, Union[yaml.nodes.MappingNode, yaml.nodes.ScalarNode, yaml.nodes.SequenceNode], bool) -> syntax.FieldListEntry
+ """Parse an entry in a generic argument or generic reply field list in the IDL file."""
+ entry = syntax.FieldListEntry(ctxt.file_name, node.start_mark.line, node.start_mark.column)
+ entry.name = name
+
+ if is_generic_argument_field_list:
+ mapping_rules = {"forward_to_shards": _RuleDesc("bool_scalar")}
+ else:
+ mapping_rules = {"forward_from_shards": _RuleDesc("bool_scalar")}
+
+ _generic_parser(ctxt, node, "field", entry, mapping_rules)
+ return entry
+
+
+def _parse_field_list_entries(ctxt, node, is_generic_argument_field_list):
+ # type: (errors.ParserContext, yaml.nodes.MappingNode, bool) -> List[syntax.FieldListEntry]
+ """Parse a fields section in a field list in the IDL file."""
+
+ entries = []
+
+ field_name_set = set() # type: Set[str]
+
+ for [first_node, second_node] in node.value:
+
+ first_name = first_node.value
+
+ if first_name in field_name_set:
+ ctxt.add_duplicate_error(first_node, first_name)
+ continue
+ entry = _parse_field_list_entry(ctxt, first_name, second_node,
+ is_generic_argument_field_list)
+ entries.append(entry)
+
+ field_name_set.add(first_name)
+
+ return entries
+
+
+def _parse_generic_argument_list_entries(ctxt, node):
+ # type: (errors.ParserContext, yaml.nodes.MappingNode) -> List[syntax.FieldListEntry]
+ """Parse a fields section in a generic argument list in the IDL file."""
+ return _parse_field_list_entries(ctxt, node, True)
+
+
+def _parse_generic_reply_field_list_entries(ctxt, node):
+ # type: (errors.ParserContext, yaml.nodes.MappingNode) -> List[syntax.FieldListEntry]
+ """Parse a fields section in a generic reply field list in the IDL file."""
+ return _parse_field_list_entries(ctxt, node, False)
+
+
def _parse_enum_values(ctxt, node):
# type: (errors.ParserContext, yaml.nodes.MappingNode) -> List[syntax.EnumValue]
"""Parse a values section in an enum in the IDL file."""
@@ -535,8 +632,6 @@ def _parse_command(ctxt, spec, name, node):
"api_version": _RuleDesc('scalar'),
"is_deprecated": _RuleDesc('bool_scalar'),
"unstable": _RuleDesc("bool_scalar"),
- "forward_to_shards": _RuleDesc("bool_scalar"),
- "forward_from_shards": _RuleDesc("bool_scalar"),
"strict": _RuleDesc("bool_scalar"),
"inline_chained_structs": _RuleDesc("bool_scalar"),
"immutable": _RuleDesc('bool_scalar'),
@@ -770,6 +865,12 @@ def _parse(stream, error_file_name):
_parse_mapping(ctxt, spec, second_node, 'structs', _parse_struct)
elif first_name == "commands":
_parse_mapping(ctxt, spec, second_node, 'commands', _parse_command)
+ elif first_name == "generic_argument_lists":
+ _parse_mapping(ctxt, spec, second_node, 'generic_argument_lists',
+ _parse_generic_argument_list)
+ elif first_name == "generic_reply_field_lists":
+ _parse_mapping(ctxt, spec, second_node, 'generic_reply_field_lists',
+ _parse_generic_reply_field_list)
elif first_name == "server_parameters":
_parse_mapping(ctxt, spec, second_node, "server_parameters", _parse_server_parameter)
elif first_name == "configs":
diff --git a/buildscripts/idl/idl/syntax.py b/buildscripts/idl/idl/syntax.py
index 2969a300791..159e0b31800 100644
--- a/buildscripts/idl/idl/syntax.py
+++ b/buildscripts/idl/idl/syntax.py
@@ -113,6 +113,8 @@ class SymbolTable(object):
self.enums = [] # type: List[Enum]
self.structs = [] # type: List[Struct]
self.types = [] # type: List[Type]
+ self.generic_argument_lists = [] # type: List[GenericArgumentList]
+ self.generic_reply_field_lists = [] # type: List[GenericReplyFieldList]
def _is_duplicate(self, ctxt, location, name, duplicate_class_name):
# type: (errors.ParserContext, common.SourceLocation, str, str) -> bool
@@ -122,6 +124,8 @@ class SymbolTable(object):
"enum": self.enums,
"struct": self.structs,
"type": self.types,
+ "generic_argument_list": self.generic_argument_lists,
+ "generic_reply_field_list": self.generic_reply_field_lists,
}):
if item.name == name:
ctxt.add_duplicate_symbol_error(location, name, duplicate_class_name, entity_type)
@@ -158,6 +162,18 @@ class SymbolTable(object):
if not self._is_duplicate(ctxt, command, command.name, "command"):
self.commands.append(command)
+ def add_generic_argument_list(self, ctxt, field_list):
+ # type: (errors.ParserContext, GenericArgumentList) -> None
+ """Add an IDL generic argument list to the symbol table and check for duplicates."""
+ if not self._is_duplicate(ctxt, field_list, field_list.name, "generic_argument_list"):
+ self.generic_argument_lists.append(field_list)
+
+ def add_generic_reply_field_list(self, ctxt, field_list):
+ # type: (errors.ParserContext, GenericReplyFieldList) -> None
+ """Add an IDL generic reply field list to the symbol table and check for duplicates."""
+ if not self._is_duplicate(ctxt, field_list, field_list.name, "generic_reply_field_list"):
+ self.generic_reply_field_lists.append(field_list)
+
def add_imported_symbol_table(self, ctxt, imported_symbols):
# type: (errors.ParserContext, SymbolTable) -> None
"""
@@ -420,16 +436,47 @@ class Command(Struct):
# type: (str, int, int) -> None
"""Construct a Command."""
self.namespace = None # type: str
+ self.command_name = None # type: str
self.type = None # type: str
self.reply_type = None # type: str
self.api_version = "" # type: str
self.is_deprecated = False # type: bool
self.unstable = False # type: bool
+ super(Command, self).__init__(file_name, line, column)
+
+
+class FieldListBase(common.SourceLocation):
+ """IDL field list information."""
+
+ def __init__(self, file_name, line, column):
+ # type: (str, int, int) -> None
+ """Construct a FieldList."""
+ self.name = None # type: str
+ self.cpp_name = None # type: str
+ self.description = None # type: str
+ self.fields = None # type: List[FieldListEntry]
+ super(FieldListBase, self).__init__(file_name, line, column)
+
+
+class GenericArgumentList(FieldListBase):
+ """IDL generic argument list."""
+
+
+class GenericReplyFieldList(FieldListBase):
+ """IDL generic reply field list."""
+
+
+class FieldListEntry(common.SourceLocation):
+ """Options for a field in a generic argument or generic reply field list."""
+
+ def __init__(self, file_name, line, column):
+ # type: (str, int, int) -> None
+ """Construct a FieldListEntry."""
+ self.name = None # type: str
self.forward_to_shards = False # type: bool
self.forward_from_shards = False # type: bool
self.command_name = None # type: str
-
- super(Command, self).__init__(file_name, line, column)
+ super(FieldListEntry, self).__init__(file_name, line, column)
class EnumValue(common.SourceLocation):
diff --git a/buildscripts/idl/tests/test_parser.py b/buildscripts/idl/tests/test_parser.py
index 17ec8f76d9f..fac46404bc7 100644
--- a/buildscripts/idl/tests/test_parser.py
+++ b/buildscripts/idl/tests/test_parser.py
@@ -842,8 +842,6 @@ class TestParser(testcase.IDLTestcase):
api_version: 1
is_deprecated: true
unstable: true
- forward_to_shards: true
- forward_from_shards: true
immutable: true
inline_chained_structs: true
generate_comparison_operators: true
@@ -865,8 +863,6 @@ class TestParser(testcase.IDLTestcase):
api_version: 1
is_deprecated: false
unstable: false
- forward_to_shards: false
- forward_from_shards: false
immutable: false
inline_chained_structs: false
generate_comparison_operators: false
@@ -1392,6 +1388,126 @@ class TestParser(testcase.IDLTestcase):
cpp_varname: gToaster
"""), idl.errors.ERROR_ID_MISSING_REQUIRED_FIELD)
+ def _test_field_list(self, field_list_name, should_forward_name):
+ # type: (str, str) -> None
+ """Positive field list test cases."""
+
+ # Generic field with no options
+ self.assert_parse(
+ textwrap.dedent(f"""
+ {field_list_name}:
+ foo:
+ description: foo
+ fields:
+ foo: {{}}
+ """))
+
+ # All fields with true for bools
+ self.assert_parse(
+ textwrap.dedent(f"""
+ {field_list_name}:
+ foo:
+ description: foo
+ fields:
+ foo:
+ {should_forward_name}: true
+ """))
+
+ # All fields with false for bools
+ self.assert_parse(
+ textwrap.dedent(f"""
+ {field_list_name}:
+ foo:
+ description: foo
+ fields:
+ foo:
+ {should_forward_name}: false
+ """))
+
+ # cpp_name.
+ self.assert_parse(
+ textwrap.dedent(f"""
+ {field_list_name}:
+ foo:
+ description: foo
+ cpp_name: foo
+ fields:
+ foo: {{}}
+ """))
+
+ def _test_field_list_negative(self, field_list_name, should_forward_name):
+ # type: (str, str) -> None
+ """Negative field list test cases."""
+
+ # Field list as a scalar
+ self.assert_parse_fail(
+ textwrap.dedent(f"""
+ {field_list_name}:
+ foo: 1
+ """), idl.errors.ERROR_ID_IS_NODE_TYPE)
+
+ # No description
+ self.assert_parse_fail(
+ textwrap.dedent(f"""
+ {field_list_name}:
+ foo:
+ fields:
+ foo: {{}}
+ """), idl.errors.ERROR_ID_MISSING_REQUIRED_FIELD)
+
+ # Unknown option
+ self.assert_parse_fail(
+ textwrap.dedent(f"""
+ {field_list_name}:
+ foo:
+ description: foo
+ foo: bar
+ fields:
+ foo: {{}}
+ """), idl.errors.ERROR_ID_UNKNOWN_NODE)
+
+ # forward_to_shards is a bool.
+ self.assert_parse_fail(
+ textwrap.dedent(f"""
+ {field_list_name}:
+ foo:
+ description: foo
+ fields:
+ foo:
+ {should_forward_name}: asdf
+ """), idl.errors.ERROR_ID_IS_NODE_VALID_BOOL)
+
+ # forward_from_shards is a bool
+ self.assert_parse_fail(
+ textwrap.dedent(f"""
+ {field_list_name}:
+ foo:
+ description: foo
+ fields:
+ foo:
+ {should_forward_name}: asdf
+ """), idl.errors.ERROR_ID_IS_NODE_VALID_BOOL)
+
+ def test_generic_arguments_list(self):
+ # type: () -> None
+ """Positive generic argument list test cases."""
+ self._test_field_list("generic_argument_lists", "forward_to_shards")
+
+ def test_generic_arguments_list_negative(self):
+ # type: () -> None
+ """Negative generic argument list test cases."""
+ self._test_field_list_negative("generic_argument_lists", "forward_to_shards")
+
+ def test_generic_reply_fields_list(self):
+ # type: () -> None
+ """Positive generic reply fields list test cases."""
+ self._test_field_list("generic_reply_field_lists", "forward_from_shards")
+
+ def test_generic_reply_fields_list_negative(self):
+ # type: () -> None
+ """Negative generic reply fields list test cases."""
+ self._test_field_list_negative("generic_reply_field_lists", "forward_from_shards")
+
if __name__ == '__main__':
diff --git a/src/mongo/bson/util/bson_check.h b/src/mongo/bson/util/bson_check.h
index b969ab7a2a6..2b5fe87e0b4 100644
--- a/src/mongo/bson/util/bson_check.h
+++ b/src/mongo/bson/util/bson_check.h
@@ -30,9 +30,9 @@
#pragma once
#include "mongo/base/status.h"
-#include "mongo/db/command_generic_argument.h"
#include "mongo/db/commands.h"
#include "mongo/db/jsobj.h"
+#include "mongo/idl/command_generic_argument.h"
#include "mongo/util/str.h"
#include "mongo/util/string_map.h"
diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript
index a5f45c339db..9866cb342e1 100644
--- a/src/mongo/db/SConscript
+++ b/src/mongo/db/SConscript
@@ -574,11 +574,11 @@ env.Library(
],
LIBDEPS_PRIVATE=[
'$BUILD_DIR/mongo/bson/mutable/mutable_bson',
+ '$BUILD_DIR/mongo/idl/idl_parser',
'$BUILD_DIR/mongo/rpc/command_status',
'$BUILD_DIR/mongo/rpc/rpc',
'audit',
'auth/auth',
- 'command_generic_argument',
'commands/server_status_core',
'commands/test_commands_enabled',
'namespace_string',
@@ -586,17 +586,6 @@ env.Library(
)
env.Library(
- target='command_generic_argument',
- source=[
- 'command_generic_argument.cpp',
- ],
- LIBDEPS=[
- '$BUILD_DIR/mongo/base',
- '$BUILD_DIR/mongo/db/commands/server_status_core',
- ],
-)
-
-env.Library(
target='command_can_run_here',
source=[
'command_can_run_here.cpp',
diff --git a/src/mongo/db/auth/user_management_commands_parser.cpp b/src/mongo/db/auth/user_management_commands_parser.cpp
index c7de4541041..01f94f81d7a 100644
--- a/src/mongo/db/auth/user_management_commands_parser.cpp
+++ b/src/mongo/db/auth/user_management_commands_parser.cpp
@@ -44,9 +44,9 @@
#include "mongo/db/auth/privilege_parser.h"
#include "mongo/db/auth/user_document_parser.h"
#include "mongo/db/auth/user_name.h"
-#include "mongo/db/command_generic_argument.h"
#include "mongo/db/commands.h"
#include "mongo/db/jsobj.h"
+#include "mongo/idl/command_generic_argument.h"
#include "mongo/stdx/unordered_set.h"
#include "mongo/util/str.h"
diff --git a/src/mongo/db/catalog/SConscript b/src/mongo/db/catalog/SConscript
index 452c371aa71..84c782e70a8 100644
--- a/src/mongo/db/catalog/SConscript
+++ b/src/mongo/db/catalog/SConscript
@@ -20,9 +20,9 @@ env.Library(
],
LIBDEPS=[
'$BUILD_DIR/mongo/base',
- '$BUILD_DIR/mongo/db/command_generic_argument',
'$BUILD_DIR/mongo/db/commands/create_command',
'$BUILD_DIR/mongo/db/query/collation/collator_interface',
+ '$BUILD_DIR/mongo/idl/idl_parser',
],
)
diff --git a/src/mongo/db/catalog/coll_mod.cpp b/src/mongo/db/catalog/coll_mod.cpp
index 69c72434180..9ba87dda47b 100644
--- a/src/mongo/db/catalog/coll_mod.cpp
+++ b/src/mongo/db/catalog/coll_mod.cpp
@@ -40,7 +40,6 @@
#include "mongo/db/catalog/collection_options.h"
#include "mongo/db/catalog/index_catalog.h"
#include "mongo/db/client.h"
-#include "mongo/db/command_generic_argument.h"
#include "mongo/db/concurrency/write_conflict_exception.h"
#include "mongo/db/curop_failpoint_helpers.h"
#include "mongo/db/db_raii.h"
@@ -57,6 +56,7 @@
#include "mongo/db/storage/durable_catalog.h"
#include "mongo/db/storage/recovery_unit.h"
#include "mongo/db/views/view_catalog.h"
+#include "mongo/idl/command_generic_argument.h"
#include "mongo/logv2/log.h"
#include "mongo/util/fail_point.h"
diff --git a/src/mongo/db/catalog/collection_options.cpp b/src/mongo/db/catalog/collection_options.cpp
index 9ddf2b0e336..49338f3f504 100644
--- a/src/mongo/db/catalog/collection_options.cpp
+++ b/src/mongo/db/catalog/collection_options.cpp
@@ -34,10 +34,10 @@
#include <algorithm>
#include "mongo/base/string_data.h"
-#include "mongo/db/command_generic_argument.h"
#include "mongo/db/commands.h"
#include "mongo/db/query/collation/collator_factory_interface.h"
#include "mongo/db/query/collation/collator_interface.h"
+#include "mongo/idl/command_generic_argument.h"
#include "mongo/util/str.h"
namespace mongo {
diff --git a/src/mongo/db/catalog/create_collection.cpp b/src/mongo/db/catalog/create_collection.cpp
index 9b10c11b822..27143d697ee 100644
--- a/src/mongo/db/catalog/create_collection.cpp
+++ b/src/mongo/db/catalog/create_collection.cpp
@@ -36,7 +36,6 @@
#include "mongo/bson/bsonobj.h"
#include "mongo/db/catalog/collection_catalog.h"
#include "mongo/db/catalog/database_holder.h"
-#include "mongo/db/command_generic_argument.h"
#include "mongo/db/commands.h"
#include "mongo/db/concurrency/write_conflict_exception.h"
#include "mongo/db/curop.h"
@@ -48,6 +47,7 @@
#include "mongo/db/ops/insert.h"
#include "mongo/db/repl/replication_coordinator.h"
#include "mongo/db/views/view_catalog.h"
+#include "mongo/idl/command_generic_argument.h"
#include "mongo/logv2/log.h"
namespace mongo {
diff --git a/src/mongo/db/commands.cpp b/src/mongo/db/commands.cpp
index 83fba136be7..920c43113a2 100644
--- a/src/mongo/db/commands.cpp
+++ b/src/mongo/db/commands.cpp
@@ -47,7 +47,6 @@
#include "mongo/db/auth/authorization_session.h"
#include "mongo/db/auth/privilege.h"
#include "mongo/db/client.h"
-#include "mongo/db/command_generic_argument.h"
#include "mongo/db/commands/test_commands_enabled.h"
#include "mongo/db/curop.h"
#include "mongo/db/error_labels.h"
@@ -55,6 +54,7 @@
#include "mongo/db/namespace_string.h"
#include "mongo/db/read_write_concern_defaults.h"
#include "mongo/idl/basic_types_gen.h"
+#include "mongo/idl/command_generic_argument.h"
#include "mongo/idl/idl_parser.h"
#include "mongo/logv2/log.h"
#include "mongo/rpc/factory.h"
diff --git a/src/mongo/db/commands/SConscript b/src/mongo/db/commands/SConscript
index c676bb2453f..5f61a1dc421 100644
--- a/src/mongo/db/commands/SConscript
+++ b/src/mongo/db/commands/SConscript
@@ -87,7 +87,6 @@ env.Library(
],
LIBDEPS=[
'$BUILD_DIR/mongo/base',
- '$BUILD_DIR/mongo/db/command_generic_argument',
'$BUILD_DIR/mongo/db/namespace_string',
],
LIBDEPS_PRIVATE=[
diff --git a/src/mongo/db/commands/create_indexes.cpp b/src/mongo/db/commands/create_indexes.cpp
index 01689109e7f..37ee281f48a 100644
--- a/src/mongo/db/commands/create_indexes.cpp
+++ b/src/mongo/db/commands/create_indexes.cpp
@@ -43,7 +43,6 @@
#include "mongo/db/catalog/index_key_validate.h"
#include "mongo/db/catalog/multi_index_block.h"
#include "mongo/db/catalog/uncommitted_collections.h"
-#include "mongo/db/command_generic_argument.h"
#include "mongo/db/commands.h"
#include "mongo/db/concurrency/d_concurrency.h"
#include "mongo/db/concurrency/write_conflict_exception.h"
@@ -61,6 +60,7 @@
#include "mongo/db/server_options.h"
#include "mongo/db/storage/two_phase_index_build_knobs_gen.h"
#include "mongo/db/views/view_catalog.h"
+#include "mongo/idl/command_generic_argument.h"
#include "mongo/logv2/log.h"
#include "mongo/platform/compiler.h"
#include "mongo/s/shard_key_pattern.h"
diff --git a/src/mongo/db/commands/current_op_common.cpp b/src/mongo/db/commands/current_op_common.cpp
index 8071cf75bf9..d1a08443e20 100644
--- a/src/mongo/db/commands/current_op_common.cpp
+++ b/src/mongo/db/commands/current_op_common.cpp
@@ -33,9 +33,9 @@
#include <string>
-#include "mongo/db/command_generic_argument.h"
#include "mongo/db/commands/test_commands_enabled.h"
#include "mongo/db/namespace_string.h"
+#include "mongo/idl/command_generic_argument.h"
namespace mongo {
namespace {
diff --git a/src/mongo/db/commands/dbcheck.cpp b/src/mongo/db/commands/dbcheck.cpp
index 73b4b1cf00d..41a52193ba3 100644
--- a/src/mongo/db/commands/dbcheck.cpp
+++ b/src/mongo/db/commands/dbcheck.cpp
@@ -35,7 +35,6 @@
#include "mongo/db/catalog/collection_catalog.h"
#include "mongo/db/catalog/database.h"
#include "mongo/db/catalog/health_log.h"
-#include "mongo/db/command_generic_argument.h"
#include "mongo/db/commands.h"
#include "mongo/db/commands/test_commands_enabled.h"
#include "mongo/db/concurrency/write_conflict_exception.h"
@@ -45,6 +44,7 @@
#include "mongo/db/repl/dbcheck.h"
#include "mongo/db/repl/oplog.h"
#include "mongo/db/repl/optime.h"
+#include "mongo/idl/command_generic_argument.h"
#include "mongo/util/background.h"
#include "mongo/logv2/log.h"
diff --git a/src/mongo/db/commands/get_last_error.cpp b/src/mongo/db/commands/get_last_error.cpp
index f55358ab8c4..d2b45e78498 100644
--- a/src/mongo/db/commands/get_last_error.cpp
+++ b/src/mongo/db/commands/get_last_error.cpp
@@ -32,7 +32,6 @@
#include "mongo/platform/basic.h"
#include "mongo/db/client.h"
-#include "mongo/db/command_generic_argument.h"
#include "mongo/db/commands.h"
#include "mongo/db/curop.h"
#include "mongo/db/field_parser.h"
@@ -41,6 +40,7 @@
#include "mongo/db/repl/repl_client_info.h"
#include "mongo/db/repl/replication_coordinator.h"
#include "mongo/db/write_concern.h"
+#include "mongo/idl/command_generic_argument.h"
#include "mongo/logv2/log.h"
namespace mongo {
diff --git a/src/mongo/db/commands/parameters.cpp b/src/mongo/db/commands/parameters.cpp
index d6ade3f7a18..ececa325557 100644
--- a/src/mongo/db/commands/parameters.cpp
+++ b/src/mongo/db/commands/parameters.cpp
@@ -38,11 +38,11 @@
#include "mongo/client/replica_set_monitor.h"
#include "mongo/config.h"
#include "mongo/db/auth/authorization_manager.h"
-#include "mongo/db/command_generic_argument.h"
#include "mongo/db/commands.h"
#include "mongo/db/commands/parameters_gen.h"
#include "mongo/db/commands/parse_log_component_settings.h"
#include "mongo/db/storage/storage_options.h"
+#include "mongo/idl/command_generic_argument.h"
#include "mongo/logv2/log.h"
#include "mongo/util/str.h"
diff --git a/src/mongo/db/commands_bm.cpp b/src/mongo/db/commands_bm.cpp
index 82feaaf60f6..5c50f9c0ad2 100644
--- a/src/mongo/db/commands_bm.cpp
+++ b/src/mongo/db/commands_bm.cpp
@@ -32,7 +32,7 @@
#include <benchmark/benchmark.h>
#include "mongo/base/string_data.h"
-#include "mongo/db/command_generic_argument.h"
+#include "mongo/idl/command_generic_argument.h"
namespace mongo {
namespace {
diff --git a/src/mongo/db/pipeline/aggregation_request.cpp b/src/mongo/db/pipeline/aggregation_request.cpp
index 673232a3d50..7894a5eb3c2 100644
--- a/src/mongo/db/pipeline/aggregation_request.cpp
+++ b/src/mongo/db/pipeline/aggregation_request.cpp
@@ -38,7 +38,6 @@
#include "mongo/base/string_data.h"
#include "mongo/bson/bsonobjbuilder.h"
#include "mongo/db/catalog/document_validation.h"
-#include "mongo/db/command_generic_argument.h"
#include "mongo/db/commands.h"
#include "mongo/db/exec/document_value/document.h"
#include "mongo/db/exec/document_value/value.h"
@@ -46,6 +45,7 @@
#include "mongo/db/query/query_request.h"
#include "mongo/db/repl/read_concern_args.h"
#include "mongo/db/storage/storage_options.h"
+#include "mongo/idl/command_generic_argument.h"
namespace mongo {
diff --git a/src/mongo/db/query/SConscript b/src/mongo/db/query/SConscript
index 6559ae6d5be..eba0ff73786 100644
--- a/src/mongo/db/query/SConscript
+++ b/src/mongo/db/query/SConscript
@@ -128,7 +128,7 @@ env.Library(
],
LIBDEPS=[
"$BUILD_DIR/mongo/base",
- "$BUILD_DIR/mongo/db/command_generic_argument",
+ "$BUILD_DIR/mongo/idl/idl_parser",
],
)
diff --git a/src/mongo/db/query/explain_options.cpp b/src/mongo/db/query/explain_options.cpp
index bb3939710f1..772681434fc 100644
--- a/src/mongo/db/query/explain_options.cpp
+++ b/src/mongo/db/query/explain_options.cpp
@@ -32,7 +32,7 @@
#include "mongo/db/query/explain_options.h"
#include "mongo/bson/bsonobjbuilder.h"
-#include "mongo/db/command_generic_argument.h"
+#include "mongo/idl/command_generic_argument.h"
#include "mongo/util/assert_util.h"
namespace mongo {
diff --git a/src/mongo/db/query/find_and_modify_request.cpp b/src/mongo/db/query/find_and_modify_request.cpp
index 9f2a5e6d192..15193aa94f7 100644
--- a/src/mongo/db/query/find_and_modify_request.cpp
+++ b/src/mongo/db/query/find_and_modify_request.cpp
@@ -34,9 +34,9 @@
#include "mongo/base/status_with.h"
#include "mongo/bson/bsonobjbuilder.h"
#include "mongo/bson/util/bson_extract.h"
-#include "mongo/db/command_generic_argument.h"
#include "mongo/db/query/hint_parser.h"
#include "mongo/db/write_concern.h"
+#include "mongo/idl/command_generic_argument.h"
#include "mongo/idl/idl_parser.h"
namespace mongo {
diff --git a/src/mongo/db/query/getmore_request.cpp b/src/mongo/db/query/getmore_request.cpp
index 808f33d0e0b..14027c9245d 100644
--- a/src/mongo/db/query/getmore_request.cpp
+++ b/src/mongo/db/query/getmore_request.cpp
@@ -35,10 +35,10 @@
#include <boost/optional.hpp>
-#include "mongo/db/command_generic_argument.h"
#include "mongo/db/commands.h"
#include "mongo/db/namespace_string.h"
#include "mongo/db/repl/bson_extract_optime.h"
+#include "mongo/idl/command_generic_argument.h"
#include "mongo/util/assert_util.h"
#include "mongo/util/str.h"
diff --git a/src/mongo/db/query/query_request.cpp b/src/mongo/db/query/query_request.cpp
index db47e2b0605..5b22e68d650 100644
--- a/src/mongo/db/query/query_request.cpp
+++ b/src/mongo/db/query/query_request.cpp
@@ -37,11 +37,11 @@
#include "mongo/base/status_with.h"
#include "mongo/bson/simple_bsonobj_comparator.h"
#include "mongo/db/catalog/collection_catalog.h"
-#include "mongo/db/command_generic_argument.h"
#include "mongo/db/commands.h"
#include "mongo/db/dbmessage.h"
#include "mongo/db/namespace_string.h"
#include "mongo/db/repl/read_concern_args.h"
+#include "mongo/idl/command_generic_argument.h"
#include "mongo/util/assert_util.h"
#include "mongo/util/str.h"
diff --git a/src/mongo/idl/SConscript b/src/mongo/idl/SConscript
index 53ff5606def..02673e1fe93 100644
--- a/src/mongo/idl/SConscript
+++ b/src/mongo/idl/SConscript
@@ -6,11 +6,13 @@ env = env.Clone()
env.Library(
target="idl_parser",
source=[
+ 'command_generic_argument.cpp',
+ 'generic_argument.idl',
'idl_parser.cpp'
],
LIBDEPS=[
'$BUILD_DIR/mongo/base',
- '$BUILD_DIR/mongo/db/command_generic_argument',
+ '$BUILD_DIR/mongo/db/commands/server_status_core',
]
)
@@ -59,6 +61,7 @@ env.CppUnitTest(
target='idl_test',
source=[
'config_option_test.cpp',
+ 'command_generic_argument_test.cpp',
'feature_flag_test.cpp',
'idl_test.cpp',
'server_parameter_specialized_test.cpp',
diff --git a/src/mongo/idl/command_generic_argument.cpp b/src/mongo/idl/command_generic_argument.cpp
new file mode 100644
index 00000000000..9c6ebb2a23b
--- /dev/null
+++ b/src/mongo/idl/command_generic_argument.cpp
@@ -0,0 +1,56 @@
+/**
+ * Copyright (C) 2018-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/idl/generic_argument_gen.h"
+
+namespace mongo {
+
+bool isGenericArgument(StringData arg) {
+ return Generic_args_api_v1::hasField(arg) || Generic_args_unstable_v1::hasField(arg);
+}
+
+// TODO(SERVER-51848): rename shouldForwardToShards
+bool isRequestStripArgument(StringData arg) {
+ return !(Generic_args_api_v1::shouldForwardToShards(arg) &&
+ Generic_args_unstable_v1::shouldForwardToShards(arg));
+}
+
+// TODO(SERVER-51848): rename shouldForwardFromShards
+bool isReplyStripArgument(StringData arg) {
+ return !(Generic_reply_fields_api_v1::shouldForwardFromShards(arg) &&
+ Generic_reply_fields_unstable_v1::shouldForwardFromShards(arg));
+}
+
+bool isMongocryptdArgument(StringData arg) {
+ return arg == "jsonSchema"_sd;
+}
+
+} // namespace mongo
diff --git a/src/mongo/db/command_generic_argument.h b/src/mongo/idl/command_generic_argument.h
index 69d695250ec..69d695250ec 100644
--- a/src/mongo/db/command_generic_argument.h
+++ b/src/mongo/idl/command_generic_argument.h
diff --git a/src/mongo/db/command_generic_argument.cpp b/src/mongo/idl/command_generic_argument_test.cpp
index e15c2498a97..c02230d4722 100644
--- a/src/mongo/db/command_generic_argument.cpp
+++ b/src/mongo/idl/command_generic_argument_test.cpp
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2018-present MongoDB, Inc.
+ * Copyright (C) 2020-present MongoDB, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
@@ -29,18 +29,16 @@
#include "mongo/platform/basic.h"
-#include "mongo/db/command_generic_argument.h"
-#include "mongo/util/string_map.h"
-
-#include <algorithm>
-#include <array>
-#include <cstdlib>
-#include <iterator>
+#include "mongo/idl/command_generic_argument.h"
+#include "mongo/unittest/unittest.h"
namespace mongo {
+namespace test {
-namespace {
+using namespace fmt::literals;
+// A copy of the generic command arguments and reply fields from before they were moved to IDL in
+// SERVER-51848. We will test that the IDL definitions match these old C++ definitions.
struct SpecialArgRecord {
StringData name;
bool isGeneric;
@@ -48,9 +46,6 @@ struct SpecialArgRecord {
bool stripFromReply;
};
-// Not including "help" since we don't pass help requests through to the command parser.
-// If that changes, it should be added. When you add to this list, consider whether you
-// should also change the filterCommandRequestForPassthrough() function.
// clang-format off
static constexpr std::array<SpecialArgRecord, 34> specials{{
// /-isGeneric
@@ -92,36 +87,23 @@ static constexpr std::array<SpecialArgRecord, 34> specials{{
{"$topologyTime"_sd, 1, 1, 1}}};
// clang-format on
-template <bool SpecialArgRecord::*pmo>
-bool filteredSpecialsContains(StringData arg) {
- static const auto& filteredNames = *new auto([] {
- StringSet s;
- for (const auto& e : specials) {
- if (e.*pmo) {
- s.insert(e.name.toString());
- }
+TEST(CommandGenericArgument, AllGenericArgumentsAndReplyFields) {
+ for (const auto& record : specials) {
+ if (isGenericArgument(record.name) != record.isGeneric) {
+ FAIL("isGenericArgument('{}') should be {}, but it's {}"_format(
+ record.name, record.isGeneric, isGenericArgument(record.name)));
}
- return s;
- }());
- return filteredNames.count(arg) > 0;
-}
-
-} // namespace
-bool isGenericArgument(StringData arg) {
- return filteredSpecialsContains<&SpecialArgRecord::isGeneric>(arg);
-}
-
-bool isRequestStripArgument(StringData arg) {
- return filteredSpecialsContains<&SpecialArgRecord::stripFromRequest>(arg);
-}
-
-bool isReplyStripArgument(StringData arg) {
- return filteredSpecialsContains<&SpecialArgRecord::stripFromReply>(arg);
-}
+ if (isRequestStripArgument(record.name) != record.stripFromRequest) {
+ FAIL("isRequestStripArgument('{}') should be {}, but it's {}"_format(
+ record.name, record.stripFromRequest, isRequestStripArgument(record.name)));
+ }
-bool isMongocryptdArgument(StringData arg) {
- return arg == "jsonSchema"_sd;
+ if (isReplyStripArgument(record.name) != record.stripFromReply) {
+ FAIL("isReplyStripArgument('{}') should be {}, but it's {}"_format(
+ record.name, record.stripFromReply, isReplyStripArgument(record.name)));
+ }
+ }
}
-
+} // namespace test
} // namespace mongo
diff --git a/src/mongo/idl/generic_argument.idl b/src/mongo/idl/generic_argument.idl
new file mode 100644
index 00000000000..c384e52f34d
--- /dev/null
+++ b/src/mongo/idl/generic_argument.idl
@@ -0,0 +1,130 @@
+# Copyright (C) 2020-present MongoDB, Inc.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the Server Side Public License, version 1,
+# as published by MongoDB, Inc.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# Server Side Public License for more details.
+#
+# You should have received a copy of the Server Side Public License
+# along with this program. If not, see
+# <http://www.mongodb.com/licensing/server-side-public-license>.
+#
+# As a special exception, the copyright holders give permission to link the
+# code of portions of this program with the OpenSSL library under certain
+# conditions as described in each individual source file and distribute
+# linked combinations including the program with the OpenSSL library. You
+# must comply with the Server Side Public License in all respects for
+# all of the code used other than as permitted herein. If you modify file(s)
+# with this exception, you may extend this exception to your version of the
+# file(s), but you are not obligated to do so. If you do not wish to do so,
+# delete this exception statement from your version. If you delete this
+# exception statement from all source files in the program, then also delete
+# it in the license file.
+#
+global:
+ cpp_namespace: "mongo"
+
+# This file lists the generic arguments accepted by all commands, and generic
+# reply fields that all commands may return.
+
+generic_argument_lists:
+ generic_args_api_v1:
+ description: "Arguments accepted by all commands. These are guaranteed backwards-compatible for as long as the server supports API Version 1."
+ fields:
+ apiVersion:
+ forward_to_shards: false
+ apiStrict:
+ forward_to_shards: false
+ apiDeprecationErrors:
+ forward_to_shards: false
+ $db:
+ forward_to_shards: false
+ maxTimeMS:
+ forward_to_shards: true
+ readConcern:
+ forward_to_shards: true
+ writeConcern:
+ forward_to_shards: true
+ lsid:
+ forward_to_shards: true
+ clientOperationKey:
+ forward_to_shards: true
+ txnNumber:
+ forward_to_shards: true
+ autocommit:
+ forward_to_shards: true
+ startTransaction:
+ forward_to_shards: true
+ stmtId:
+ forward_to_shards: true
+ comment:
+ forward_to_shards: true
+ $readPreference:
+ forward_to_shards: false
+ $clusterTime:
+ forward_to_shards: false
+
+ generic_args_unstable_v1:
+ description: "Arguments accepted by all commands. These are not guaranteed backwards-compatible."
+ fields:
+ $audit:
+ forward_to_shards: false
+ $client:
+ forward_to_shards: false
+ $configServerState:
+ forward_to_shards: false
+ allowImplicitCollectionCreation:
+ forward_to_shards: false
+ $oplogQueryData:
+ forward_to_shards: false
+ $queryOptions:
+ forward_to_shards: true
+ $replData:
+ forward_to_shards: false
+ databaseVersion:
+ forward_to_shards: false
+ shardVersion:
+ forward_to_shards: false
+ tracking_info:
+ forward_to_shards: false
+ coordinator:
+ forward_to_shards: true
+ maxTimeMSOpOnly:
+ forward_to_shards: false
+ $configTime:
+ forward_to_shards: false
+ $topologyTime:
+ forward_to_shards: false
+
+generic_reply_field_lists:
+ generic_reply_fields_api_v1:
+ description: "Fields that may appear in any command reply. These are guaranteed backwards-compatible for as long as the server supports API Version 1."
+ fields:
+ $clusterTime:
+ forward_from_shards: false
+ operationTime:
+ forward_from_shards: false
+
+ generic_reply_fields_unstable_v1:
+ description: "Fields that may appear in any command reply. These are not guaranteed backwards-compatible."
+ fields:
+ $configServerState:
+ forward_from_shards: false
+ $gleStats:
+ forward_from_shards: false
+ lastCommittedOpTime:
+ forward_from_shards: false
+ readOnly:
+ forward_from_shards: false
+ $configTime:
+ forward_from_shards: false
+ $replData:
+ forward_from_shards: false
+ $topologyTime:
+ forward_from_shards: false
+ $oplogQueryData:
+ forward_from_shards: false
diff --git a/src/mongo/idl/idl_parser.cpp b/src/mongo/idl/idl_parser.cpp
index e78ce27b161..36c07fafe9a 100644
--- a/src/mongo/idl/idl_parser.cpp
+++ b/src/mongo/idl/idl_parser.cpp
@@ -34,7 +34,7 @@
#include "mongo/idl/idl_parser.h"
#include "mongo/bson/bsonobjbuilder.h"
-#include "mongo/db/command_generic_argument.h"
+#include "mongo/idl/command_generic_argument.h"
#include "mongo/util/str.h"
namespace mongo {
diff --git a/src/mongo/idl/unittest.idl b/src/mongo/idl/unittest.idl
index 39a75b949e4..b792eefa8df 100644
--- a/src/mongo/idl/unittest.idl
+++ b/src/mongo/idl/unittest.idl
@@ -805,3 +805,30 @@ commands:
command_name: CommandWithOkReply
namespace: ignored
reply_type: OkReply
+
+
+##################################################################################################
+#
+# Test field lists
+#
+##################################################################################################
+
+generic_argument_lists:
+ my_generic_args_list:
+ description: UnitTest for generic arguments list
+ fields:
+ my_generic_field_0:
+ forward_to_shards: false
+ my_generic_field_1:
+ forward_to_shards: true
+ my_generic_field_3: {}
+
+generic_reply_field_lists:
+ my_generic_reply_field_list:
+ description: UnitTest for generic reply fields list
+ fields:
+ my_generic_field_0:
+ forward_from_shards: false
+ my_generic_field_1:
+ forward_from_shards: true
+ my_generic_field_3: {}
diff --git a/src/mongo/s/commands/cluster_explain.cpp b/src/mongo/s/commands/cluster_explain.cpp
index 8c4cdb5e699..c720235474c 100644
--- a/src/mongo/s/commands/cluster_explain.cpp
+++ b/src/mongo/s/commands/cluster_explain.cpp
@@ -30,9 +30,9 @@
#include "mongo/platform/basic.h"
#include "mongo/bson/bsonmisc.h"
-#include "mongo/db/command_generic_argument.h"
#include "mongo/db/commands.h"
#include "mongo/db/query/explain_common.h"
+#include "mongo/idl/command_generic_argument.h"
#include "mongo/rpc/get_status_from_command_result.h"
#include "mongo/s/client/shard_registry.h"
#include "mongo/s/commands/cluster_explain.h"
diff --git a/src/mongo/s/commands/cluster_explain_cmd.cpp b/src/mongo/s/commands/cluster_explain_cmd.cpp
index 4d17e5ebd19..ce2fb30491c 100644
--- a/src/mongo/s/commands/cluster_explain_cmd.cpp
+++ b/src/mongo/s/commands/cluster_explain_cmd.cpp
@@ -29,9 +29,9 @@
#include "mongo/platform/basic.h"
-#include "mongo/db/command_generic_argument.h"
#include "mongo/db/commands.h"
#include "mongo/db/query/explain.h"
+#include "mongo/idl/command_generic_argument.h"
#include "mongo/s/query/cluster_find.h"
namespace mongo {