summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavis Haupt <davis.haupt@mongodb.com>2023-04-18 13:37:47 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2023-04-18 14:49:46 +0000
commite6d81d58334a2a15be92de7ea65cb0ae61c62e35 (patch)
treefab91413e0e72fecf228cca2ea5cde81c1bba6cb
parent518f98088c22598a12b33aa3457253d9fdcea0cc (diff)
downloadmongo-e6d81d58334a2a15be92de7ea65cb0ae61c62e35.tar.gz
SERVER-75138 Add shapification for DocumentSources which use IDL for serialization
-rw-r--r--buildscripts/idl/idl/ast.py7
-rw-r--r--buildscripts/idl/idl/binder.py18
-rw-r--r--buildscripts/idl/idl/errors.py10
-rw-r--r--buildscripts/idl/idl/generator.py8
-rw-r--r--buildscripts/idl/idl/parser.py2
-rw-r--r--buildscripts/idl/idl/syntax.py2
-rw-r--r--buildscripts/idl/tests/test_binder.py46
-rw-r--r--src/mongo/db/SConscript15
-rw-r--r--src/mongo/db/pipeline/SConscript2
-rw-r--r--src/mongo/db/pipeline/document_source_coll_stats.cpp5
-rw-r--r--src/mongo/db/pipeline/document_source_coll_stats.idl12
-rw-r--r--src/mongo/db/pipeline/document_source_coll_stats_test.cpp117
-rw-r--r--src/mongo/db/pipeline/document_source_exchange.cpp5
-rw-r--r--src/mongo/db/pipeline/document_source_exchange_test.cpp26
-rw-r--r--src/mongo/db/pipeline/document_source_internal_all_collection_stats.cpp10
-rw-r--r--src/mongo/db/pipeline/document_source_internal_all_collection_stats.idl2
-rw-r--r--src/mongo/db/pipeline/document_source_list_cached_and_active_users.h3
-rw-r--r--src/mongo/db/pipeline/document_source_list_local_sessions.h5
-rw-r--r--src/mongo/db/pipeline/document_source_list_sessions.cpp5
-rw-r--r--src/mongo/db/pipeline/document_source_list_sessions.idl14
-rw-r--r--src/mongo/db/pipeline/document_source_merge.cpp1
-rw-r--r--src/mongo/db/pipeline/document_source_set_variable_from_subpipeline.cpp2
-rw-r--r--src/mongo/db/pipeline/exchange_spec.idl8
-rw-r--r--src/mongo/db/pipeline/storage_stats_spec.idl5
-rw-r--r--src/mongo/db/query/query_shape_test.idl4
-rw-r--r--src/mongo/db/s/document_source_analyze_shard_key_read_write_distribution.cpp2
-rw-r--r--src/mongo/db/s/resharding/document_source_resharding_ownership_match.cpp2
-rw-r--r--src/mongo/s/query/document_source_merge_cursors.cpp2
28 files changed, 280 insertions, 60 deletions
diff --git a/buildscripts/idl/idl/ast.py b/buildscripts/idl/idl/ast.py
index a07d547248e..a3808963213 100644
--- a/buildscripts/idl/idl/ast.py
+++ b/buildscripts/idl/idl/ast.py
@@ -109,6 +109,9 @@ class Type(common.SourceLocation):
self.first_element_field_name = None # type: str
self.deserialize_with_tenant = False # type: bool
self.internal_only = False # type: bool
+ # Marks whether this type is a query shape component.
+ # Can only be true if is_struct is true.
+ self.is_query_shape_component = False # type: bool
super(Type, self).__init__(file_name, line, column)
@@ -251,14 +254,14 @@ class Field(common.SourceLocation):
# See WRITING-13831 for details on query shape.
self.query_shape_literal = None # type: Optional[bool]
# Determines whether or not this field represents a fieldpath that should be anonymized.
- self.query_shape_fieldpath = None # type: Optional[bool]
+ self.query_shape_anonymize = None # type: Optional[bool]
super(Field, self).__init__(file_name, line, column)
@property
def should_serialize_query_shape(self):
# type: () -> bool
- return self.query_shape_fieldpath or self.query_shape_literal
+ return self.query_shape_anonymize or self.query_shape_literal
class Privilege(common.SourceLocation):
diff --git a/buildscripts/idl/idl/binder.py b/buildscripts/idl/idl/binder.py
index da4191d83fc..179768e6df1 100644
--- a/buildscripts/idl/idl/binder.py
+++ b/buildscripts/idl/idl/binder.py
@@ -327,17 +327,17 @@ def _bind_struct_common(ctxt, parsed_spec, struct, ast_struct):
# Verify that each field on the struct defines a query shape type on the field if and only if
# query_shape_component is defined on the struct.
- defined_query_shape_type = ast_field.query_shape_literal is not None or ast_field.query_shape_fieldpath is not None
+ defined_query_shape_type = ast_field.query_shape_literal is not None or ast_field.query_shape_anonymize is not None
if not field.hidden and struct.query_shape_component and not defined_query_shape_type:
ctxt.add_must_declare_shape_type(ast_field, ast_struct.name, ast_field.name)
if not struct.query_shape_component and defined_query_shape_type:
ctxt.add_must_be_query_shape_component(ast_field, ast_struct.name, ast_field.name)
- if ast_field.query_shape_fieldpath and ast_field.type.cpp_type not in [
+ if ast_field.query_shape_anonymize and ast_field.type.cpp_type not in [
"std::string", "std::vector<std::string>"
]:
- ctxt.add_query_shape_fieldpath_must_be_string(ast_field, ast_field.name,
+ ctxt.add_query_shape_anonymize_must_be_string(ast_field, ast_field.name,
ast_field.type.cpp_type)
# Fill out the field comparison_order property as needed
@@ -455,6 +455,7 @@ def _bind_struct_type(struct):
ast_type.cpp_type = _get_struct_qualified_cpp_name(struct)
ast_type.bson_serialization_type = ["object"]
ast_type.first_element_field_name = struct.fields[0].name if struct.fields else None
+ ast_type.is_query_shape_component = struct.query_shape_component
return ast_type
@@ -1019,6 +1020,7 @@ def _bind_type(idltype):
ast_type.deserializer = _normalize_method_name(idltype.cpp_type, idltype.deserializer)
ast_type.deserialize_with_tenant = idltype.deserialize_with_tenant
ast_type.internal_only = idltype.internal_only
+ ast_type.is_query_shape_component = True
return ast_type
@@ -1045,7 +1047,7 @@ def _bind_field(ctxt, parsed_spec, field):
ast_field.always_serialize = field.always_serialize
ast_field.preparse = field.preparse
ast_field.query_shape_literal = field.query_shape_literal
- ast_field.query_shape_fieldpath = field.query_shape_fieldpath
+ ast_field.query_shape_anonymize = field.query_shape_anonymize
ast_field.cpp_name = field.name
if field.cpp_name:
@@ -1056,11 +1058,11 @@ def _bind_field(ctxt, parsed_spec, field):
ctxt.add_array_not_valid_error(ast_field, "field", ast_field.name)
# Validate that 'field' is not both a query shape literal and query shape fieldpath. The two are mutually exclusive.
- if ast_field.query_shape_literal is not None and ast_field.query_shape_fieldpath is not None:
+ if ast_field.query_shape_literal is not None and ast_field.query_shape_anonymize is not None:
ctxt.add_field_cannot_be_literal_and_fieldpath(ast_field, ast_field.name)
- if ast_field.query_shape_fieldpath is False:
- ctxt.add_field_cannot_have_query_shape_fieldpath_false(ast_field)
+ if ast_field.query_shape_anonymize is False:
+ ctxt.add_field_cannot_have_query_shape_anonymize_false(ast_field)
if field.ignore:
ast_field.ignore = field.ignore
@@ -1134,6 +1136,8 @@ def _bind_field(ctxt, parsed_spec, field):
if ast_field.validator is None:
return None
+ if ast_field.should_serialize_query_shape and not ast_field.type.is_query_shape_component:
+ ctxt.add_must_be_query_shape_component(ast_field, ast_field.type.name, ast_field.name)
return ast_field
diff --git a/buildscripts/idl/idl/errors.py b/buildscripts/idl/idl/errors.py
index e9386535ffe..5837cb11648 100644
--- a/buildscripts/idl/idl/errors.py
+++ b/buildscripts/idl/idl/errors.py
@@ -950,10 +950,10 @@ class ParserContext(object):
def add_must_declare_shape_type(self, location, struct_name, field_name):
# type: (common.SourceLocation, str, str) -> None
- """Add an error about a field not specifying either query_shape_literal or query_shape_fieldpath if the struct is query_shape_component."""
+ """Add an error about a field not specifying either query_shape_literal or query_shape_anonymize if the struct is query_shape_component."""
self._add_error(
location, ERROR_ID_FIELD_MUST_DECLARE_SHAPE_LITERAL,
- f"Field '{field_name}' must specify either 'query_shape_literal' or 'query_shape_fieldpath' since struct '{struct_name}' is a query shape component."
+ f"Field '{field_name}' must specify either 'query_shape_literal' or 'query_shape_anonymize' since struct '{struct_name}' is a query shape component."
)
def add_must_be_query_shape_component(self, location, struct_name, field_name):
@@ -963,7 +963,7 @@ class ParserContext(object):
f"Field '{field_name}' cannot specify 'query_shape_literal' property since struct '{struct_name}' is not a query shape component."
)
- def add_query_shape_fieldpath_must_be_string(self, location, field_name, field_type):
+ def add_query_shape_anonymize_must_be_string(self, location, field_name, field_type):
self._add_error(
location, ERROR_ID_INVALID_TYPE_FOR_SHAPIFY,
f"In order for {field_name} to be marked as a query shape fieldpath, it must have a string type, not {field_type}."
@@ -975,9 +975,9 @@ class ParserContext(object):
f"{field_name} cannot be marked as both a query shape literal and query shape fieldpath."
)
- def add_field_cannot_have_query_shape_fieldpath_false(self, location):
+ def add_field_cannot_have_query_shape_anonymize_false(self, location):
self._add_error(location, ERROR_ID_QUERY_SHAPE_FIELDPATH_CANNOT_BE_FALSE,
- "'query_shape_fieldpath' cannot be defined as false if it is set.")
+ "'query_shape_anonymize' cannot be defined as false if it is set.")
def _assert_unique_error_messages():
diff --git a/buildscripts/idl/idl/generator.py b/buildscripts/idl/idl/generator.py
index b472ffe17a6..26792609546 100644
--- a/buildscripts/idl/idl/generator.py
+++ b/buildscripts/idl/idl/generator.py
@@ -2166,7 +2166,7 @@ class _CppSourceFileWriter(_CppFileWriterBase):
self._writer.write_template(
'options.serializeLiteralValue(${expression}).serializeForIDL(${field_name}, builder);'
)
- elif field.query_shape_fieldpath:
+ elif field.query_shape_anonymize:
self._writer.write_template(
'builder->append(${field_name}, options.serializeFieldPathFromString(${expression}));'
)
@@ -2321,7 +2321,7 @@ class _CppSourceFileWriter(_CppFileWriterBase):
self._writer.write_template(
'options.serializeLiteralValue(${expression}).serializeForIDL(${field_name}, builder);'
)
- elif field.query_shape_fieldpath:
+ elif field.query_shape_anonymize:
self._writer.write_template(
'builder->append(${field_name}, options.serializeFieldPathFromString(${expression}));'
)
@@ -2335,7 +2335,7 @@ class _CppSourceFileWriter(_CppFileWriterBase):
self._writer.write_template(
'options.serializeLiteralValue(value).serializeForIDL(${field_name}, builder);'
)
- elif field.query_shape_fieldpath:
+ elif field.query_shape_anonymize:
self._writer.write_template(
'idl::idlSerialize(builder, ${field_name}, options.serializeFieldPathFromString(value));'
)
@@ -2382,7 +2382,7 @@ class _CppSourceFileWriter(_CppFileWriterBase):
self._writer.write_line(
'options.serializeLiteralValue(%s).serializeForIDL(%s, builder);' %
(_access_member(field), _get_field_constant_name(field)))
- elif field.query_shape_fieldpath:
+ elif field.query_shape_anonymize:
self._writer.write_line(
'builder->append(%s, options.serializeFieldPathFromString(%s));' %
(_get_field_constant_name(field), _access_member(field)))
diff --git a/buildscripts/idl/idl/parser.py b/buildscripts/idl/idl/parser.py
index cc4f047231e..022ad0dd3f9 100644
--- a/buildscripts/idl/idl/parser.py
+++ b/buildscripts/idl/idl/parser.py
@@ -408,7 +408,7 @@ def _parse_field(ctxt, name, node):
_RuleDesc('bool_scalar'),
"query_shape_literal":
_RuleDesc('required_bool_scalar'),
- "query_shape_fieldpath":
+ "query_shape_anonymize":
_RuleDesc('required_bool_scalar'),
})
diff --git a/buildscripts/idl/idl/syntax.py b/buildscripts/idl/idl/syntax.py
index c8b3573294f..f161488062e 100644
--- a/buildscripts/idl/idl/syntax.py
+++ b/buildscripts/idl/idl/syntax.py
@@ -494,7 +494,7 @@ class Field(common.SourceLocation):
self.constructed = False # type: bool
self.query_shape_literal = None # type: Optional[bool]
- self.query_shape_fieldpath = None # type: Optional[bool]
+ self.query_shape_anonymize = None # type: Optional[bool]
self.hidden = False # type: bool
diff --git a/buildscripts/idl/tests/test_binder.py b/buildscripts/idl/tests/test_binder.py
index a891c6f15b2..c4e3c806d71 100644
--- a/buildscripts/idl/tests/test_binder.py
+++ b/buildscripts/idl/tests/test_binder.py
@@ -2704,7 +2704,7 @@ class TestBinder(testcase.IDLTestcase):
query_shape_literal: false
"""), idl.errors.ERROR_ID_CANNOT_DECLARE_SHAPE_LITERAL)
- # Validating query_shape_fieldpath relies on std::string
+ # Validating query_shape_anonymize relies on std::string
basic_types = textwrap.dedent("""
types:
string:
@@ -2731,7 +2731,7 @@ class TestBinder(testcase.IDLTestcase):
description: ""
fields:
field1:
- query_shape_fieldpath: true
+ query_shape_anonymize: true
type: string
field2:
query_shape_literal: false
@@ -2746,7 +2746,7 @@ class TestBinder(testcase.IDLTestcase):
description: ""
fields:
field1:
- query_shape_fieldpath: true
+ query_shape_anonymize: true
type: array<string>
field2:
query_shape_literal: false
@@ -2762,7 +2762,7 @@ class TestBinder(testcase.IDLTestcase):
description: ""
fields:
field1:
- query_shape_fieldpath: false
+ query_shape_anonymize: false
type: string
field2:
query_shape_literal: false
@@ -2778,7 +2778,7 @@ class TestBinder(testcase.IDLTestcase):
description: ""
fields:
field1:
- query_shape_fieldpath: true
+ query_shape_anonymize: true
type: bool
field2:
query_shape_literal: false
@@ -2794,7 +2794,7 @@ class TestBinder(testcase.IDLTestcase):
description: ""
fields:
field1:
- query_shape_fieldpath: true
+ query_shape_anonymize: true
type: array<bool>
field2:
query_shape_literal: false
@@ -2810,7 +2810,7 @@ class TestBinder(testcase.IDLTestcase):
description: ""
fields:
field1:
- query_shape_fieldpath: true
+ query_shape_anonymize: true
query_shape_literal: true
type: string
field2:
@@ -2818,6 +2818,38 @@ class TestBinder(testcase.IDLTestcase):
type: bool
"""), idl.errors.ERROR_ID_CANNOT_BE_LITERAL_AND_FIELDPATH)
+ self.assert_bind_fail(
+ basic_types + textwrap.dedent("""
+ structs:
+ StructZero:
+ strict: true
+ description: ""
+ fields:
+ field1:
+ query_shape_literal: true
+ type: string
+ """), idl.errors.ERROR_ID_CANNOT_DECLARE_SHAPE_LITERAL)
+
+ self.assert_bind_fail(
+ basic_types + textwrap.dedent("""
+ structs:
+ StructZero:
+ strict: true
+ description: ""
+ fields:
+ field1:
+ type: string
+ struct1:
+ query_shape_component: true
+ strict: true
+ description: ""
+ fields:
+ field2:
+ type: StructZero
+ description: ""
+ query_shape_literal: true
+ """), idl.errors.ERROR_ID_CANNOT_DECLARE_SHAPE_LITERAL)
+
if __name__ == '__main__':
diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript
index 29ba3357afd..82737ca855e 100644
--- a/src/mongo/db/SConscript
+++ b/src/mongo/db/SConscript
@@ -1609,7 +1609,6 @@ env.Library(
'pipeline/make_js_function.cpp',
'pipeline/monotonic_expression.cpp',
'pipeline/variables.cpp',
- 'query/serialization_options.cpp',
],
LIBDEPS=[
'$BUILD_DIR/mongo/bson/util/bson_extract',
@@ -1635,6 +1634,7 @@ env.Library(
'query/collation/collator_interface',
'query/datetime/date_time_support',
'query/query_knobs',
+ 'serialization_options',
'stats/counters',
'update/pattern_cmp',
],
@@ -1647,6 +1647,19 @@ env.Library(
)
env.Library(
+ target='serialization_options',
+ source=[
+ 'query/serialization_options.cpp',
+ ],
+ LIBDEPS=[
+ '$BUILD_DIR/mongo/base',
+ '$BUILD_DIR/mongo/db/exec/document_value/document_value',
+ '$BUILD_DIR/mongo/db/pipeline/field_path',
+ ],
+ LIBDEPS_PRIVATE=[],
+)
+
+env.Library(
target='startup_recovery',
source=[
'repair.cpp',
diff --git a/src/mongo/db/pipeline/SConscript b/src/mongo/db/pipeline/SConscript
index d20c1f0f99c..75bbde6b153 100644
--- a/src/mongo/db/pipeline/SConscript
+++ b/src/mongo/db/pipeline/SConscript
@@ -496,6 +496,7 @@ env.Library(
LIBDEPS=[
'$BUILD_DIR/mongo/base',
'$BUILD_DIR/mongo/db/exec/document_value/document_value',
+ '$BUILD_DIR/mongo/db/serialization_options',
'$BUILD_DIR/mongo/db/storage/key_string',
'$BUILD_DIR/mongo/idl/idl_parser',
'$BUILD_DIR/mongo/s/common_s',
@@ -593,6 +594,7 @@ env.CppUnitTest(
'document_source_change_stream_add_post_image_test.cpp',
'document_source_change_stream_test.cpp',
'document_source_check_resume_token_test.cpp',
+ 'document_source_coll_stats_test.cpp',
'document_source_count_test.cpp',
'document_source_current_op_test.cpp',
'document_source_densify_test.cpp',
diff --git a/src/mongo/db/pipeline/document_source_coll_stats.cpp b/src/mongo/db/pipeline/document_source_coll_stats.cpp
index 96fd6d1c48a..0facc2283ef 100644
--- a/src/mongo/db/pipeline/document_source_coll_stats.cpp
+++ b/src/mongo/db/pipeline/document_source_coll_stats.cpp
@@ -132,10 +132,7 @@ DocumentSource::GetNextResult DocumentSourceCollStats::doGetNext() {
}
Value DocumentSourceCollStats::serialize(SerializationOptions opts) const {
- if (opts.redactIdentifiers || opts.replacementForLiteralArgs) {
- MONGO_UNIMPLEMENTED_TASSERT(7484352);
- }
- return Value(Document{{getSourceName(), _collStatsSpec.toBSON()}});
+ return Value(Document{{getSourceName(), _collStatsSpec.toBSON(opts)}});
}
} // namespace mongo
diff --git a/src/mongo/db/pipeline/document_source_coll_stats.idl b/src/mongo/db/pipeline/document_source_coll_stats.idl
index 18efd4d2c7f..0f146db02cf 100644
--- a/src/mongo/db/pipeline/document_source_coll_stats.idl
+++ b/src/mongo/db/pipeline/document_source_coll_stats.idl
@@ -39,36 +39,46 @@ structs:
LatencyStatsSpec:
description: Represents the 'latencyStats' argument to the $collStats stage.
strict: true
- fields:
+ query_shape_component: true
+ fields:
histograms:
description: Adds latency histogram information to the embedded documents in latencyStats if true.
type: optionalBool
+ # Do not abstract this literal, since it is parameterizing the stage like an enum rather than representing
+ # real user input.
+ query_shape_literal: false
DocumentSourceCollStatsSpec:
description: Specification for a $collStats stage.
strict: true
+ query_shape_component: true
fields:
latencyStats:
description: A request to include latency stats in the $collStats output.
type: LatencyStatsSpec
optional: true
+ query_shape_literal: true
storageStats:
description: Adds storage statistics to the return document.
type: StorageStatsSpec
optional: true
+ query_shape_literal: true
count:
description: Adds the total number of documents in the collection to the return document.
type: object
validator:
callback: validateObjectIsEmpty
optional: true
+ query_shape_literal: true
queryExecStats:
description: Adds query execution statistics to the return document.
type: object
validator:
callback: validateObjectIsEmpty
optional: true
+ query_shape_literal: true
$_requestOnTimeseriesView:
description: When set to true, $collStats stage requests statistics from the view namespace.
When set to false, $collStats stage requests statistics from the underlying collection.
cpp_name: requestOnTimeseriesView
type: optionalBool
+ query_shape_literal: false
diff --git a/src/mongo/db/pipeline/document_source_coll_stats_test.cpp b/src/mongo/db/pipeline/document_source_coll_stats_test.cpp
new file mode 100644
index 00000000000..87b7473b47a
--- /dev/null
+++ b/src/mongo/db/pipeline/document_source_coll_stats_test.cpp
@@ -0,0 +1,117 @@
+/**
+ * Copyright (C) 2023-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/db/pipeline/aggregation_context_fixture.h"
+#include "mongo/db/pipeline/document_source_coll_stats.h"
+#include "mongo/db/pipeline/document_source_coll_stats_gen.h"
+#include "mongo/db/pipeline/expression_context_for_test.h"
+#include "mongo/unittest/bson_test_util.h"
+#include "mongo/unittest/unittest.h"
+
+namespace mongo {
+namespace {
+using DocumentSourceCollStatsTest = AggregationContextFixture;
+TEST_F(DocumentSourceCollStatsTest, QueryShape) {
+ auto spec = DocumentSourceCollStatsSpec();
+
+ auto stage = make_intrusive<DocumentSourceCollStats>(getExpCtx(), spec);
+ ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
+ R"({"$collStats":{}})",
+ redact(*stage));
+
+ spec.setCount(BSONObj());
+ spec.setQueryExecStats(BSONObj());
+ stage = make_intrusive<DocumentSourceCollStats>(getExpCtx(), spec);
+ ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
+ R"({"$collStats":{"count":"?","queryExecStats":"?"}})",
+ redact(*stage));
+
+ auto latencyStats = LatencyStatsSpec();
+ latencyStats.setHistograms(true);
+ spec.setLatencyStats(latencyStats);
+ stage = make_intrusive<DocumentSourceCollStats>(getExpCtx(), spec);
+ ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
+ R"({
+ "$collStats": {
+ "latencyStats": {
+ "histograms": true
+ },
+ "count": "?",
+ "queryExecStats": "?"
+ }
+ })",
+ redact(*stage));
+
+ auto storageStats = StorageStatsSpec();
+ storageStats.setScale(2);
+ storageStats.setVerbose(true);
+ spec.setStorageStats(storageStats);
+ stage = make_intrusive<DocumentSourceCollStats>(getExpCtx(), spec);
+ ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
+ R"({
+ "$collStats": {
+ "latencyStats": {
+ "histograms": true
+ },
+ "storageStats": {
+ "scale": "?",
+ "verbose": true,
+ "waitForLock": true,
+ "numericOnly": false
+ },
+ "count": "?",
+ "queryExecStats": "?"
+ }
+ })",
+ redact(*stage));
+
+ storageStats.setWaitForLock(false);
+ storageStats.setNumericOnly(false);
+ spec.setStorageStats(storageStats);
+ stage = make_intrusive<DocumentSourceCollStats>(getExpCtx(), spec);
+ ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
+ R"({
+ "$collStats": {
+ "latencyStats": {
+ "histograms": true
+ },
+ "storageStats": {
+ "scale": "?",
+ "verbose": true,
+ "waitForLock": false,
+ "numericOnly": false
+ },
+ "count": "?",
+ "queryExecStats": "?"
+ }
+ })",
+ redact(*stage));
+}
+} // namespace
+} // namespace mongo
diff --git a/src/mongo/db/pipeline/document_source_exchange.cpp b/src/mongo/db/pipeline/document_source_exchange.cpp
index 2d6999f4a7d..81d7beecf64 100644
--- a/src/mongo/db/pipeline/document_source_exchange.cpp
+++ b/src/mongo/db/pipeline/document_source_exchange.cpp
@@ -94,10 +94,7 @@ const char* DocumentSourceExchange::getSourceName() const {
}
Value DocumentSourceExchange::serialize(SerializationOptions opts) const {
- if (opts.redactIdentifiers || opts.replacementForLiteralArgs) {
- MONGO_UNIMPLEMENTED_TASSERT(7484348);
- }
- return Value(DOC(getSourceName() << _exchange->getSpec().toBSON()));
+ return Value(DOC(getSourceName() << _exchange->getSpec().toBSON(opts)));
}
DocumentSourceExchange::DocumentSourceExchange(
diff --git a/src/mongo/db/pipeline/document_source_exchange_test.cpp b/src/mongo/db/pipeline/document_source_exchange_test.cpp
index 6063c252dbd..a454dd283fe 100644
--- a/src/mongo/db/pipeline/document_source_exchange_test.cpp
+++ b/src/mongo/db/pipeline/document_source_exchange_test.cpp
@@ -749,4 +749,30 @@ TEST_F(DocumentSourceExchangeTest, RejectInvalidMissingKeys) {
Exchange(parseSpec(spec), Pipeline::create({}, getExpCtx())), AssertionException, 50967);
}
+TEST_F(DocumentSourceExchangeTest, QueryShape) {
+ const size_t nDocs = 500;
+
+ auto source = getMockSource(nDocs);
+
+ ExchangeSpec spec;
+ spec.setPolicy(ExchangePolicyEnum::kRoundRobin);
+ spec.setConsumers(1);
+ spec.setBufferSize(1024);
+ boost::intrusive_ptr<Exchange> ex = new Exchange(spec, Pipeline::create({source}, getExpCtx()));
+ boost::intrusive_ptr<DocumentSourceExchange> stage =
+ new DocumentSourceExchange(getExpCtx(), ex, 0, nullptr);
+
+ ASSERT_BSONOBJ_EQ_AUTO( //
+ R"({
+ "$_internalExchange": {
+ "policy": "roundrobin",
+ "consumers": "?",
+ "orderPreserving": false,
+ "bufferSize": "?",
+ "key": "?"
+ }
+ })",
+ redact(*stage));
+}
+
} // namespace mongo
diff --git a/src/mongo/db/pipeline/document_source_internal_all_collection_stats.cpp b/src/mongo/db/pipeline/document_source_internal_all_collection_stats.cpp
index 5eb4e7625aa..38ba92a7a01 100644
--- a/src/mongo/db/pipeline/document_source_internal_all_collection_stats.cpp
+++ b/src/mongo/db/pipeline/document_source_internal_all_collection_stats.cpp
@@ -129,12 +129,9 @@ Pipeline::SourceContainer::iterator DocumentSourceInternalAllCollectionStats::do
void DocumentSourceInternalAllCollectionStats::serializeToArray(std::vector<Value>& array,
SerializationOptions opts) const {
auto explain = opts.verbosity;
- if (opts.redactIdentifiers || opts.replacementForLiteralArgs) {
- MONGO_UNIMPLEMENTED_TASSERT(7484341);
- }
if (explain) {
BSONObjBuilder bob;
- _internalAllCollectionStatsSpec.serialize(&bob);
+ _internalAllCollectionStatsSpec.serialize(&bob, opts);
if (_absorbedMatch) {
bob.append("match", _absorbedMatch->getQuery());
}
@@ -170,9 +167,6 @@ const char* DocumentSourceInternalAllCollectionStats::getSourceName() const {
}
Value DocumentSourceInternalAllCollectionStats::serialize(SerializationOptions opts) const {
- if (opts.redactIdentifiers || opts.replacementForLiteralArgs) {
- MONGO_UNIMPLEMENTED_TASSERT(7484340);
- }
- return Value(Document{{getSourceName(), _internalAllCollectionStatsSpec.toBSON()}});
+ return Value(Document{{getSourceName(), _internalAllCollectionStatsSpec.toBSON(opts)}});
}
} // namespace mongo
diff --git a/src/mongo/db/pipeline/document_source_internal_all_collection_stats.idl b/src/mongo/db/pipeline/document_source_internal_all_collection_stats.idl
index bb02e8b1954..8c3c43e637d 100644
--- a/src/mongo/db/pipeline/document_source_internal_all_collection_stats.idl
+++ b/src/mongo/db/pipeline/document_source_internal_all_collection_stats.idl
@@ -40,8 +40,10 @@ structs:
DocumentSourceInternalAllCollectionStatsSpec:
description: Specification for an $_internalAllCollectionStats stage.
strict: true
+ query_shape_component: true
fields:
stats:
description: Specification for a $collStats stage.
type: DocumentSourceCollStatsSpec
optional: true
+ query_shape_literal: true
diff --git a/src/mongo/db/pipeline/document_source_list_cached_and_active_users.h b/src/mongo/db/pipeline/document_source_list_cached_and_active_users.h
index d86611ac874..2603db75aae 100644
--- a/src/mongo/db/pipeline/document_source_list_cached_and_active_users.h
+++ b/src/mongo/db/pipeline/document_source_list_cached_and_active_users.h
@@ -88,9 +88,6 @@ public:
}
Value serialize(SerializationOptions opts = SerializationOptions()) const final override {
- if (opts.redactIdentifiers || opts.replacementForLiteralArgs) {
- MONGO_UNIMPLEMENTED_TASSERT(7484330);
- }
return Value(Document{{getSourceName(), Document{}}});
}
diff --git a/src/mongo/db/pipeline/document_source_list_local_sessions.h b/src/mongo/db/pipeline/document_source_list_local_sessions.h
index 1244c2fa0ff..a9f258113da 100644
--- a/src/mongo/db/pipeline/document_source_list_local_sessions.h
+++ b/src/mongo/db/pipeline/document_source_list_local_sessions.h
@@ -102,10 +102,7 @@ public:
}
Value serialize(SerializationOptions opts = SerializationOptions()) const final override {
- if (opts.redactIdentifiers || opts.replacementForLiteralArgs) {
- MONGO_UNIMPLEMENTED_TASSERT(7484328);
- }
- return Value(Document{{getSourceName(), _spec.toBSON()}});
+ return Value(Document{{getSourceName(), _spec.toBSON(opts)}});
}
StageConstraints constraints(Pipeline::SplitState pipeState) const final {
diff --git a/src/mongo/db/pipeline/document_source_list_sessions.cpp b/src/mongo/db/pipeline/document_source_list_sessions.cpp
index d8a0ca3fe91..6eb4fdf2819 100644
--- a/src/mongo/db/pipeline/document_source_list_sessions.cpp
+++ b/src/mongo/db/pipeline/document_source_list_sessions.cpp
@@ -75,14 +75,11 @@ boost::intrusive_ptr<DocumentSource> DocumentSourceListSessions::createFromBson(
}
Value DocumentSourceListSessions::serialize(SerializationOptions opts) const {
- if (opts.redactIdentifiers || opts.replacementForLiteralArgs) {
- MONGO_UNIMPLEMENTED_TASSERT(7484327);
- }
ListSessionsSpec spec;
spec.setAllUsers(_allUsers);
spec.setUsers(_users);
spec.setPredicate(_predicate);
- return Value(Document{{getSourceName(), spec.toBSON()}});
+ return Value(Document{{getSourceName(), spec.toBSON(opts)}});
}
} // namespace mongo
diff --git a/src/mongo/db/pipeline/document_source_list_sessions.idl b/src/mongo/db/pipeline/document_source_list_sessions.idl
index e919fbda2bd..5fcdeb000d4 100644
--- a/src/mongo/db/pipeline/document_source_list_sessions.idl
+++ b/src/mongo/db/pipeline/document_source_list_sessions.idl
@@ -40,21 +40,31 @@ structs:
description: "A struct representing a $listSessions/$listLocalSessions User"
strict: true
generate_comparison_operators: true
+ query_shape_component: true
fields:
- user: string
- db: string
+ user:
+ type: string
+ query_shape_anonymize: true
+ db:
+ type: string
+ query_shape_anonymize: true
ListSessionsSpec:
description: "$listSessions and $listLocalSessions pipeline spec"
strict: true
+ query_shape_component: true
fields:
allUsers:
type: bool
default: false
+ # This boolean parameterizes the stage rather than representing user input, so do not abstract the literal.
+ query_shape_literal: false
users:
type: array<ListSessionsUser>
optional: true
+ query_shape_literal: true
$_internalPredicate:
cpp_name: predicate
type: object
optional: true
+ query_shape_literal: true # This is a MatchExpression predicate and could be shape-ified rather than completely abstracted.
diff --git a/src/mongo/db/pipeline/document_source_merge.cpp b/src/mongo/db/pipeline/document_source_merge.cpp
index f30b330d0cd..6cb337d73ae 100644
--- a/src/mongo/db/pipeline/document_source_merge.cpp
+++ b/src/mongo/db/pipeline/document_source_merge.cpp
@@ -536,6 +536,7 @@ boost::optional<DocumentSource::DistributedPlanLogic> DocumentSourceMerge::distr
Value DocumentSourceMerge::serialize(SerializationOptions opts) const {
auto explain = opts.verbosity;
if (opts.redactIdentifiers || opts.replacementForLiteralArgs) {
+ // TODO: SERVER-76208 support query shapification for IDL types with custom serializers.
MONGO_UNIMPLEMENTED_TASSERT(7484324);
}
diff --git a/src/mongo/db/pipeline/document_source_set_variable_from_subpipeline.cpp b/src/mongo/db/pipeline/document_source_set_variable_from_subpipeline.cpp
index 6e16e5a9d0f..965173f29d5 100644
--- a/src/mongo/db/pipeline/document_source_set_variable_from_subpipeline.cpp
+++ b/src/mongo/db/pipeline/document_source_set_variable_from_subpipeline.cpp
@@ -55,6 +55,8 @@ REGISTER_INTERNAL_DOCUMENT_SOURCE(setVariableFromSubPipeline,
Value DocumentSourceSetVariableFromSubPipeline::serialize(SerializationOptions opts) const {
if (opts.redactIdentifiers || opts.replacementForLiteralArgs) {
+ // TODO: SERVER-76208 support query shapification for IDL types like pipeline with custom
+ // serializers.
MONGO_UNIMPLEMENTED_TASSERT(7484314);
}
diff --git a/src/mongo/db/pipeline/exchange_spec.idl b/src/mongo/db/pipeline/exchange_spec.idl
index fd411056767..9bf0a5d9e84 100644
--- a/src/mongo/db/pipeline/exchange_spec.idl
+++ b/src/mongo/db/pipeline/exchange_spec.idl
@@ -46,25 +46,30 @@ enums:
structs:
ExchangeSpec:
description: "exchange aggregation request specification"
+ query_shape_component: true
fields:
policy:
type: ExchangePolicy
description: A string indicating a policy of how documents are distributed to consumers.
stability: stable
+ query_shape_literal: false
consumers:
type: int
description: Number of consumers.
stability: stable
+ query_shape_literal: true
orderPreserving:
type: bool
default: false
description: A flag indicating documents are merged while preserving the order.
stability: stable
+ query_shape_literal: false
bufferSize:
type: int
default: 16777216
description: The size of exchange buffers.
stability: stable
+ query_shape_literal: true
key:
type: object
default: "BSONObj()"
@@ -74,14 +79,17 @@ structs:
field listed here, or if any prefix of any path is multikey (i.e. an array is
encountered while traversing a path listed here), then it is by definition sent
to consumer 0.
+ query_shape_literal: true
boundaries:
type: array<object>
optional: true
description: Range/hash split points.
stability: stable
+ query_shape_literal: true
consumerIds:
type: array<int>
optional: true
description: Mapping from a range index to a consumer id.
stability: stable
+ query_shape_literal: true
diff --git a/src/mongo/db/pipeline/storage_stats_spec.idl b/src/mongo/db/pipeline/storage_stats_spec.idl
index a241dfb2b84..f74818fd729 100644
--- a/src/mongo/db/pipeline/storage_stats_spec.idl
+++ b/src/mongo/db/pipeline/storage_stats_spec.idl
@@ -36,18 +36,23 @@ structs:
StorageStatsSpec:
description: Represents the 'storageStats' argument to the $collStats stage.
strict: false
+ query_shape_component: true
fields:
scale:
description: A number to use as a scaling factor applied to reported metrics.
type: safeInt
optional: true
validator: { gte: 1 }
+ query_shape_literal: true
verbose:
type: optionalBool
default: false
+ query_shape_literal: false
waitForLock:
type: optionalBool
default: true
+ query_shape_literal: false
numericOnly:
type: optionalBool
default: false
+ query_shape_literal: false
diff --git a/src/mongo/db/query/query_shape_test.idl b/src/mongo/db/query/query_shape_test.idl
index 4617692aa0f..caea731e651 100644
--- a/src/mongo/db/query/query_shape_test.idl
+++ b/src/mongo/db/query/query_shape_test.idl
@@ -64,10 +64,10 @@ structs:
query_shape_literal: true
type: array<int>
fieldpath:
- query_shape_fieldpath: true
+ query_shape_anonymize: true
type: string
fieldpathList:
- query_shape_fieldpath: true
+ query_shape_anonymize: true
type: array<string>
ParentStruct:
diff --git a/src/mongo/db/s/document_source_analyze_shard_key_read_write_distribution.cpp b/src/mongo/db/s/document_source_analyze_shard_key_read_write_distribution.cpp
index b7db42cf4cb..271e0f9dea1 100644
--- a/src/mongo/db/s/document_source_analyze_shard_key_read_write_distribution.cpp
+++ b/src/mongo/db/s/document_source_analyze_shard_key_read_write_distribution.cpp
@@ -315,6 +315,8 @@ DocumentSourceAnalyzeShardKeyReadWriteDistribution::createFromBson(
Value DocumentSourceAnalyzeShardKeyReadWriteDistribution::serialize(
SerializationOptions opts) const {
if (opts.redactIdentifiers || opts.replacementForLiteralArgs) {
+ // TODO: SERVER-76208 support query shapification for IDL types like KeyPattern with custom
+ // serializers.
MONGO_UNIMPLEMENTED_TASSERT(7484305);
}
diff --git a/src/mongo/db/s/resharding/document_source_resharding_ownership_match.cpp b/src/mongo/db/s/resharding/document_source_resharding_ownership_match.cpp
index dd4a900f812..42056f44f65 100644
--- a/src/mongo/db/s/resharding/document_source_resharding_ownership_match.cpp
+++ b/src/mongo/db/s/resharding/document_source_resharding_ownership_match.cpp
@@ -94,6 +94,8 @@ StageConstraints DocumentSourceReshardingOwnershipMatch::constraints(
Value DocumentSourceReshardingOwnershipMatch::serialize(SerializationOptions opts) const {
if (opts.redactIdentifiers || opts.replacementForLiteralArgs) {
+ // TODO: SERVER-76208 support query shapification for IDL types like KeyPattern with custom
+ // serializers.
MONGO_UNIMPLEMENTED_TASSERT(7484302);
}
diff --git a/src/mongo/s/query/document_source_merge_cursors.cpp b/src/mongo/s/query/document_source_merge_cursors.cpp
index 9a35057412a..3ebff23110b 100644
--- a/src/mongo/s/query/document_source_merge_cursors.cpp
+++ b/src/mongo/s/query/document_source_merge_cursors.cpp
@@ -118,6 +118,8 @@ Value DocumentSourceMergeCursors::serialize(SerializationOptions opts) const {
invariant(!_blockingResultsMerger);
invariant(_armParams);
if (opts.redactIdentifiers || opts.replacementForLiteralArgs) {
+ // TODO: SERVER-76208 support query shapification for IDL types like namespacestring with
+ // custom serializers.
MONGO_UNIMPLEMENTED_TASSERT(7484301);
}