summaryrefslogtreecommitdiff
path: root/src/mongo/db/views
diff options
context:
space:
mode:
authorLucas de Castro Borges <lucas@gnuabordo.com.br>2025-02-11 15:07:35 -0300
committerLucas de Castro Borges <lucas@gnuabordo.com.br>2025-02-11 15:07:35 -0300
commit4cb8841196d0625dfa3825aa326f071cd27c7b8b (patch)
tree1682a647d4463397c119183369ae6f750d5fdcff /src/mongo/db/views
parentaa03c6362cbaa767638e6eed9b031d86dd2643d1 (diff)
parent8f0827553e09872941945a093b647a4211a9db7f (diff)
Update upstream source from tag 'upstream/6.0.0'master
Update to upstream version '6.0.0' with Debian dir 5604a80ec1c96ca76f25f40d78e6ef855abec322
Diffstat (limited to 'src/mongo/db/views')
-rw-r--r--src/mongo/db/views/SConscript1
-rw-r--r--src/mongo/db/views/durable_view_catalog.cpp121
-rw-r--r--src/mongo/db/views/durable_view_catalog.h8
-rw-r--r--src/mongo/db/views/resolved_view.cpp119
-rw-r--r--src/mongo/db/views/resolved_view.h23
-rw-r--r--src/mongo/db/views/view_catalog_helpers.cpp5
-rw-r--r--src/mongo/db/views/view_graph.cpp5
-rw-r--r--src/mongo/db/views/view_graph.h17
8 files changed, 95 insertions, 204 deletions
diff --git a/src/mongo/db/views/SConscript b/src/mongo/db/views/SConscript
index 94d6b3edd39..a16bf79890a 100644
--- a/src/mongo/db/views/SConscript
+++ b/src/mongo/db/views/SConscript
@@ -17,7 +17,6 @@ env.Library(
'$BUILD_DIR/mongo/db/audit',
'$BUILD_DIR/mongo/db/catalog/database_holder',
'$BUILD_DIR/mongo/db/multitenancy',
- '$BUILD_DIR/mongo/db/views/view_catalog_helpers',
],
)
diff --git a/src/mongo/db/views/durable_view_catalog.cpp b/src/mongo/db/views/durable_view_catalog.cpp
index e5281a8c63c..c96b7af6672 100644
--- a/src/mongo/db/views/durable_view_catalog.cpp
+++ b/src/mongo/db/views/durable_view_catalog.cpp
@@ -47,7 +47,6 @@
#include "mongo/db/operation_context.h"
#include "mongo/db/storage/record_data.h"
#include "mongo/db/tenant_database_name.h"
-#include "mongo/db/views/view_catalog_helpers.h"
#include "mongo/logv2/log.h"
#include "mongo/stdx/unordered_set.h"
#include "mongo/util/assert_util.h"
@@ -55,57 +54,6 @@
namespace mongo {
-namespace {
-void validateViewDefinitionBSON(OperationContext* opCtx,
- const BSONObj& viewDefinition,
- StringData dbName) {
- // Internal callers should always pass in a valid 'dbName' against which to compare the
- // 'viewDefinition'.
- invariant(NamespaceString::validDBName(dbName));
-
- bool valid = true;
-
- for (const BSONElement& e : viewDefinition) {
- std::string name(e.fieldName());
- valid &= name == "_id" || name == "viewOn" || name == "pipeline" || name == "collation" ||
- name == "timeseries";
- }
-
- const auto viewName = viewDefinition["_id"].str();
- const auto viewNameIsValid = NamespaceString::validCollectionComponent(viewName) &&
- NamespaceString::validDBName(nsToDatabaseSubstring(viewName));
- valid &= viewNameIsValid;
-
- // Only perform validation via NamespaceString if the collection name has been determined to
- // be valid. If not valid then the NamespaceString constructor will uassert.
- if (viewNameIsValid) {
- NamespaceString viewNss(viewName);
- valid &= viewNss.isValid() && viewNss.db() == dbName;
- }
-
- valid &= NamespaceString::validCollectionName(viewDefinition["viewOn"].str());
-
- const bool hasPipeline = viewDefinition.hasField("pipeline");
- valid &= hasPipeline;
- if (hasPipeline) {
- valid &= viewDefinition["pipeline"].type() == mongo::Array;
- }
-
- valid &= (!viewDefinition.hasField("collation") ||
- viewDefinition["collation"].type() == BSONType::Object);
-
- valid &= !viewDefinition.hasField("timeseries") ||
- viewDefinition["timeseries"].type() == BSONType::Object;
-
- uassert(ErrorCodes::InvalidViewDefinition,
- str::stream() << "found invalid view definition " << viewDefinition["_id"]
- << " while reading '"
- << NamespaceString(dbName, NamespaceString::kSystemDotViewsCollectionName)
- << "'",
- valid);
-}
-} // namespace
-
// DurableViewCatalog
void DurableViewCatalog::onExternalChange(OperationContext* opCtx, const NamespaceString& name) {
@@ -120,30 +68,6 @@ void DurableViewCatalog::onExternalChange(OperationContext* opCtx, const Namespa
catalog->reloadViews(opCtx, name.db()).ignore();
}
-Status DurableViewCatalog::onExternalInsert(OperationContext* opCtx,
- const BSONObj& doc,
- const NamespaceString& name) {
- try {
- validateViewDefinitionBSON(opCtx, doc, name.db());
- } catch (const DBException& e) {
- return e.toStatus();
- }
-
- auto catalog = CollectionCatalog::get(opCtx);
- NamespaceString viewName(doc.getStringField("_id"));
- NamespaceString viewOn(name.db(), doc.getStringField("viewOn"));
- BSONArray pipeline(doc.getObjectField("pipeline"));
- BSONObj collation(doc.getObjectField("collation"));
-
- return catalog->createView(opCtx,
- viewName,
- viewOn,
- pipeline,
- collation,
- view_catalog_helpers::validatePipeline,
- CollectionCatalog::ViewUpsertMode::kAlreadyDurableView);
-}
-
void DurableViewCatalog::onSystemViewsCollectionDrop(OperationContext* opCtx,
const NamespaceString& name) {
dassert(opCtx->lockState()->isDbLockedForMode(name.db(), MODE_IX));
@@ -203,9 +127,9 @@ void DurableViewCatalogImpl::_iterate(OperationContext* opCtx,
try {
viewDefinition = _validateViewDefinition(opCtx, record->data);
uassertStatusOK(callback(viewDefinition));
- } catch (const ExceptionFor<ErrorCodes::InvalidViewDefinition>&) {
+ } catch (const ExceptionFor<ErrorCodes::InvalidViewDefinition>& ex) {
if (lookupBehavior == ViewCatalogLookupBehavior::kValidateViews) {
- throw;
+ throw ex;
}
}
}
@@ -218,9 +142,46 @@ BSONObj DurableViewCatalogImpl::_validateViewDefinition(OperationContext* opCtx,
// decimal data even if decimal is disabled.
fassert(40224, validateBSON(recordData.data(), recordData.size()));
BSONObj viewDefinition = recordData.toBson();
- std::string dbName(_db->name().dbName());
- validateViewDefinitionBSON(opCtx, viewDefinition, dbName);
+ bool valid = true;
+
+ for (const BSONElement& e : viewDefinition) {
+ std::string name(e.fieldName());
+ valid &= name == "_id" || name == "viewOn" || name == "pipeline" || name == "collation" ||
+ name == "timeseries";
+ }
+
+ const auto viewName = viewDefinition["_id"].str();
+ const auto viewNameIsValid = NamespaceString::validCollectionComponent(viewName) &&
+ NamespaceString::validDBName(nsToDatabaseSubstring(viewName));
+ valid &= viewNameIsValid;
+
+ // Only perform validation via NamespaceString if the collection name has been determined to
+ // be valid. If not valid then the NamespaceString constructor will uassert.
+ if (viewNameIsValid) {
+ NamespaceString viewNss(viewName);
+ valid &= viewNss.isValid() && viewNss.db() == _db->name().dbName();
+ }
+
+ valid &= NamespaceString::validCollectionName(viewDefinition["viewOn"].str());
+
+ const bool hasPipeline = viewDefinition.hasField("pipeline");
+ valid &= hasPipeline;
+ if (hasPipeline) {
+ valid &= viewDefinition["pipeline"].type() == mongo::Array;
+ }
+
+ valid &= (!viewDefinition.hasField("collation") ||
+ viewDefinition["collation"].type() == BSONType::Object);
+
+ valid &= !viewDefinition.hasField("timeseries") ||
+ viewDefinition["timeseries"].type() == BSONType::Object;
+
+ uassert(ErrorCodes::InvalidViewDefinition,
+ str::stream() << "found invalid view definition " << viewDefinition["_id"]
+ << " while reading '" << _db->getSystemViewsName() << "'",
+ valid);
+
return viewDefinition;
}
diff --git a/src/mongo/db/views/durable_view_catalog.h b/src/mongo/db/views/durable_view_catalog.h
index 6b938c6f54c..0546636225d 100644
--- a/src/mongo/db/views/durable_view_catalog.h
+++ b/src/mongo/db/views/durable_view_catalog.h
@@ -68,14 +68,6 @@ public:
static void onExternalChange(OperationContext* opCtx, const NamespaceString& name);
/**
- * Thread-safe method to insert into the in-memory view catalog when there is an insert to
- * 'system.views'.
- */
- static Status onExternalInsert(OperationContext* opCtx,
- const BSONObj& doc,
- const NamespaceString& name);
-
- /**
* Thread-safe method to clear the in-memory state of the view catalog when the 'system.views'
* collection is dropped.
*/
diff --git a/src/mongo/db/views/resolved_view.cpp b/src/mongo/db/views/resolved_view.cpp
index 6795a874968..244d38a0b55 100644
--- a/src/mongo/db/views/resolved_view.cpp
+++ b/src/mongo/db/views/resolved_view.cpp
@@ -33,7 +33,6 @@
#include "mongo/base/init.h"
#include "mongo/bson/bsonobjbuilder.h"
-#include "mongo/db/pipeline/document_source_coll_stats.h"
#include "mongo/db/pipeline/document_source_index_stats.h"
#include "mongo/db/pipeline/document_source_internal_convert_bucket_index_stats.h"
#include "mongo/db/pipeline/document_source_internal_unpack_bucket.h"
@@ -92,22 +91,11 @@ ResolvedView ResolvedView::fromBSON(const BSONObj& commandResponseObj) {
mixedSchema = boost::optional<bool>(mixedSchemaElem.boolean());
}
- boost::optional<bool> usesExtendedRange = boost::none;
- if (auto usesExtendedRangeElem = viewDef[kTimeseriesUsesExtendedRange]) {
- uassert(6646910,
- str::stream() << "view definition must have " << kTimeseriesUsesExtendedRange
- << " of type bool or no such field",
- usesExtendedRangeElem.type() == BSONType::Bool);
-
- usesExtendedRange = boost::optional<bool>(usesExtendedRangeElem.boolean());
- }
-
return {NamespaceString(viewDef["ns"].valueStringData()),
std::move(pipeline),
std::move(collationSpec),
std::move(timeseriesOptions),
- std::move(mixedSchema),
- std::move(usesExtendedRange)};
+ std::move(mixedSchema)};
}
void ResolvedView::serialize(BSONObjBuilder* builder) const {
@@ -121,10 +109,6 @@ void ResolvedView::serialize(BSONObjBuilder* builder) const {
// Only serialize if it doesn't contain mixed data.
if ((_timeseriesMayContainMixedData && !(*_timeseriesMayContainMixedData)))
subObj.append(kTimeseriesMayContainMixedData, *_timeseriesMayContainMixedData);
-
- if ((_timeseriesUsesExtendedRange && (*_timeseriesUsesExtendedRange)))
- subObj.append(kTimeseriesUsesExtendedRange, *_timeseriesUsesExtendedRange);
-
if (!_defaultCollation.isEmpty()) {
subObj.append("collation", _defaultCollation);
}
@@ -134,48 +118,46 @@ std::shared_ptr<const ErrorExtraInfo> ResolvedView::parse(const BSONObj& cmdRepl
return std::make_shared<ResolvedView>(fromBSON(cmdReply));
}
-void ResolvedView::handleTimeseriesRewrites(std::vector<BSONObj>* resolvedPipeline) const {
- // Stages that are constrained to be the first stage of the pipeline ($collStats, $indexStats)
- // require special handling since $_internalUnpackBucket is the first stage.
- if (resolvedPipeline->size() >= 2 &&
- (*resolvedPipeline)[0][DocumentSourceInternalUnpackBucket::kStageNameInternal] &&
- ((*resolvedPipeline)[1][DocumentSourceIndexStats::kStageName] ||
- (*resolvedPipeline)[1][DocumentSourceCollStats::kStageName])) {
- // Normally for a regular read, $_internalUnpackBucket unpacks the buckets entries into
- // time-series document format and then passes the time-series documents on through the
- // pipeline. Instead, for $indexStats, we need to read the buckets collection's index
- // stats unmodified and then pass the results through an additional stage to specially
- // convert them to the time-series collection's schema, and then onward. We grab the
- // $_internalUnpackBucket stage's time-series collection schema options and pass them
- // into the $_internalConvertBucketIndexStats stage to use for schema conversion.
- if ((*resolvedPipeline)[1][DocumentSourceIndexStats::kStageName]) {
- auto unpackStage = (*resolvedPipeline)[0];
- (*resolvedPipeline)[0] = (*resolvedPipeline)[1];
- BSONObjBuilder builder;
- for (const auto& elem :
- unpackStage[DocumentSourceInternalUnpackBucket::kStageNameInternal].Obj()) {
- if (elem.fieldNameStringData() == timeseries::kTimeFieldName ||
- elem.fieldNameStringData() == timeseries::kMetaFieldName) {
- builder.append(elem);
- }
- }
- (*resolvedPipeline)[1] =
- BSON(DocumentSourceInternalConvertBucketIndexStats::kStageName << builder.obj());
- } else {
- auto collStatsStage = (*resolvedPipeline)[1];
- BSONObjBuilder builder;
- for (const auto& elem : collStatsStage[DocumentSourceCollStats::kStageName].Obj()) {
+AggregateCommandRequest ResolvedView::asExpandedViewAggregation(
+ const AggregateCommandRequest& request) const {
+ // Perform the aggregation on the resolved namespace. The new pipeline consists of two parts:
+ // first, 'pipeline' in this ResolvedView; then, the pipeline in 'request'.
+ std::vector<BSONObj> resolvedPipeline;
+ resolvedPipeline.reserve(_pipeline.size() + request.getPipeline().size());
+ resolvedPipeline.insert(resolvedPipeline.end(), _pipeline.begin(), _pipeline.end());
+ resolvedPipeline.insert(
+ resolvedPipeline.end(), request.getPipeline().begin(), request.getPipeline().end());
+
+ // $indexStats needs special handling for time-series-collections. Normally for a regular read,
+ // $_internalUnpackBucket unpacks the buckets entries into time-series document format and then
+ // passes the time-series documents on through the pipeline. Instead we need to read the buckets
+ // collection's index stats unmodified and then pass the results through an additional stage to
+ // specially convert them to the time-series collection's schema, and then onward. There is no
+ // need for the $_internalUnpackBucket stage with $indexStats, so we remove it.
+ if (resolvedPipeline.size() >= 2 &&
+ resolvedPipeline[0][DocumentSourceInternalUnpackBucket::kStageNameInternal] &&
+ resolvedPipeline[1][DocumentSourceIndexStats::kStageName]) {
+ // Clear the $_internalUnpackBucket stage.
+ auto unpackStage = resolvedPipeline[0];
+ resolvedPipeline[0] = resolvedPipeline[1];
+
+ // Grab the $_internalUnpackBucket stage's time-series collection schema options and pass
+ // them into the $_internalConvertBucketIndexStats stage to use for schema conversion.
+ BSONObjBuilder builder;
+ for (const auto& elem :
+ unpackStage[DocumentSourceInternalUnpackBucket::kStageNameInternal].Obj()) {
+ if (elem.fieldNameStringData() == timeseries::kTimeFieldName ||
+ elem.fieldNameStringData() == timeseries::kMetaFieldName) {
builder.append(elem);
}
- builder.append("$_requestOnTimeseriesView", true);
- (*resolvedPipeline)[1] = BSON(DocumentSourceCollStats::kStageName << builder.obj());
- // For $collStats, we directly read the collection stats from the buckets
- // collection, and skip $_internalUnpackBucket.
- resolvedPipeline->erase(resolvedPipeline->begin());
}
- } else if (serverGlobalParams.featureCompatibility.isGreaterThanOrEqualTo(
+ resolvedPipeline[1] =
+ BSON(DocumentSourceInternalConvertBucketIndexStats::kStageName << builder.obj());
+ } else if (resolvedPipeline.size() >= 1 &&
+ resolvedPipeline[0][DocumentSourceInternalUnpackBucket::kStageNameInternal] &&
+ serverGlobalParams.featureCompatibility.isGreaterThanOrEqualTo(
multiversion::FeatureCompatibilityVersion::kVersion_5_2)) {
- auto unpackStage = (*resolvedPipeline)[0];
+ auto unpackStage = resolvedPipeline[0];
BSONObjBuilder builder;
for (const auto& elem :
@@ -184,31 +166,11 @@ void ResolvedView::handleTimeseriesRewrites(std::vector<BSONObj>* resolvedPipeli
}
builder.append(DocumentSourceInternalUnpackBucket::kAssumeNoMixedSchemaData,
((_timeseriesMayContainMixedData && !(*_timeseriesMayContainMixedData))));
-
- builder.append(DocumentSourceInternalUnpackBucket::kUsesExtendedRange,
- ((_timeseriesUsesExtendedRange && *_timeseriesUsesExtendedRange)));
-
- (*resolvedPipeline)[0] =
+ resolvedPipeline[0] =
BSON(DocumentSourceInternalUnpackBucket::kStageNameInternal << builder.obj());
}
-}
-
-AggregateCommandRequest ResolvedView::asExpandedViewAggregation(
- const AggregateCommandRequest& request) const {
- // Perform the aggregation on the resolved namespace. The new pipeline consists of two parts:
- // first, 'pipeline' in this ResolvedView; then, the pipeline in 'request'.
- std::vector<BSONObj> resolvedPipeline;
- resolvedPipeline.reserve(_pipeline.size() + request.getPipeline().size());
- resolvedPipeline.insert(resolvedPipeline.end(), _pipeline.begin(), _pipeline.end());
- resolvedPipeline.insert(
- resolvedPipeline.end(), request.getPipeline().begin(), request.getPipeline().end());
-
- if (resolvedPipeline.size() >= 1 &&
- resolvedPipeline[0][DocumentSourceInternalUnpackBucket::kStageNameInternal]) {
- handleTimeseriesRewrites(&resolvedPipeline);
- }
- AggregateCommandRequest expandedRequest{_namespace, std::move(resolvedPipeline)};
+ AggregateCommandRequest expandedRequest{_namespace, resolvedPipeline};
if (request.getExplain()) {
expandedRequest.setExplain(request.getExplain());
@@ -230,9 +192,6 @@ AggregateCommandRequest ResolvedView::asExpandedViewAggregation(
}
}
expandedRequest.setHint(rewritten);
-
- // JLR Possibly need to handle inappropriately forcing clustered here. Will likely be
- // handled by removing the _id predicate, however.
} else {
expandedRequest.setHint(request.getHint());
}
diff --git a/src/mongo/db/views/resolved_view.h b/src/mongo/db/views/resolved_view.h
index a68ed1d5b1e..c0b3ae3d503 100644
--- a/src/mongo/db/views/resolved_view.h
+++ b/src/mongo/db/views/resolved_view.h
@@ -48,19 +48,15 @@ public:
std::vector<BSONObj> pipeline,
BSONObj defaultCollation,
boost::optional<TimeseriesOptions> timeseriesOptions = boost::none,
- boost::optional<bool> timeseriesMayContainMixedData = boost::none,
- boost::optional<bool> timeseriesUsesExtendedRange = boost::none)
+ boost::optional<bool> timeseriesMayContainMixedData = boost::none)
: _namespace(collectionNs),
_pipeline(std::move(pipeline)),
_defaultCollation(std::move(defaultCollation)),
_timeseriesOptions(timeseriesOptions),
- _timeseriesMayContainMixedData(timeseriesMayContainMixedData),
- _timeseriesUsesExtendedRange(timeseriesUsesExtendedRange) {}
+ _timeseriesMayContainMixedData(timeseriesMayContainMixedData) {}
static ResolvedView fromBSON(const BSONObj& commandResponseObj);
- void handleTimeseriesRewrites(std::vector<BSONObj>* resolvedPipeline) const;
-
/**
* Convert an aggregation command on a view to the equivalent command against the view's
* underlying collection.
@@ -80,16 +76,10 @@ public:
return _defaultCollation;
}
- bool timeseries() const {
- return _timeseriesOptions.has_value();
- }
-
// ErrorExtraInfo API
static constexpr auto code = ErrorCodes::CommandOnShardedViewNotSupportedOnMongod;
static constexpr StringData kTimeseriesMayContainMixedData = "timeseriesMayContainMixedData"_sd;
static constexpr StringData kTimeseriesOptions = "timeseriesOptions"_sd;
- static constexpr StringData kTimeseriesUsesExtendedRange = "timeseriesUsesExtendedRange"_sd;
-
void serialize(BSONObjBuilder* bob) const final;
static std::shared_ptr<const ErrorExtraInfo> parse(const BSONObj&);
@@ -97,17 +87,16 @@ private:
NamespaceString _namespace;
std::vector<BSONObj> _pipeline;
- // The default collation associated with this view. An empty object means that the default
- // is the simple collation.
+ // The default collation associated with this view. An empty object means that the default is
+ // the simple collation.
//
// Currently all operations which run over a view must use the default collation. This means
- // that operations on the view which do not specify a collation inherit the default.
- // Operations on the view which specify any other collation fail with a user error.
+ // that operations on the view which do not specify a collation inherit the default. Operations
+ // on the view which specify any other collation fail with a user error.
BSONObj _defaultCollation;
boost::optional<TimeseriesOptions> _timeseriesOptions;
boost::optional<bool> _timeseriesMayContainMixedData;
- boost::optional<bool> _timeseriesUsesExtendedRange;
};
} // namespace mongo
diff --git a/src/mongo/db/views/view_catalog_helpers.cpp b/src/mongo/db/views/view_catalog_helpers.cpp
index 04218b48ef8..018fad8437d 100644
--- a/src/mongo/db/views/view_catalog_helpers.cpp
+++ b/src/mongo/db/views/view_catalog_helpers.cpp
@@ -150,7 +150,6 @@ StatusWith<ResolvedView> resolveView(OperationContext* opCtx,
int depth = 0;
boost::optional<bool> mixedData = boost::none;
boost::optional<TimeseriesOptions> tsOptions = boost::none;
- boost::optional<bool> hasExtendedRange = boost::none;
for (; depth < ViewGraph::kMaxViewDepth; depth++) {
auto view = catalog->lookupView(opCtx, *resolvedNss);
@@ -174,8 +173,7 @@ StatusWith<ResolvedView> resolveView(OperationContext* opCtx,
std::move(resolvedPipeline),
collation ? std::move(collation.get()) : CollationSpec::kSimpleSpec,
tsOptions,
- mixedData,
- hasExtendedRange});
+ mixedData});
}
resolvedNss = &view->viewOn();
@@ -195,7 +193,6 @@ StatusWith<ResolvedView> resolveView(OperationContext* opCtx,
if (tsCollection) {
mixedData = tsCollection->getTimeseriesBucketsMayHaveMixedSchemaData();
tsOptions = tsCollection->getTimeseriesOptions();
- hasExtendedRange = tsCollection->getRequiresTimeseriesExtendedRangeSupport();
}
}
diff --git a/src/mongo/db/views/view_graph.cpp b/src/mongo/db/views/view_graph.cpp
index 716dd4e9d32..4282a0469a4 100644
--- a/src/mongo/db/views/view_graph.cpp
+++ b/src/mongo/db/views/view_graph.cpp
@@ -126,9 +126,10 @@ void ViewGraph::insertWithoutValidating(const ViewDefinition& view,
// pointers for its children.
Node* node = &(_graph[nodeId]);
invariant(node->children.empty());
+ invariant(!static_cast<bool>(node->collator));
node->size = pipelineSize;
- node->collator = CollatorInterface::cloneCollator(view.defaultCollator());
+ node->collator = view.defaultCollator();
for (const NamespaceString& childNss : refs) {
uint64_t childId = _getNodeId(childNss);
@@ -164,7 +165,7 @@ void ViewGraph::remove(const NamespaceString& viewNss) {
// This node no longer represents a view, so its children must be cleared and its collator
// unset.
node->children.clear();
- node->collator = nullptr;
+ node->collator = boost::none;
// Only remove node if there are no remaining references to this node.
if (node->parents.size() == 0) {
diff --git a/src/mongo/db/views/view_graph.h b/src/mongo/db/views/view_graph.h
index 98fefdb91dc..2653f1b5aa1 100644
--- a/src/mongo/db/views/view_graph.h
+++ b/src/mongo/db/views/view_graph.h
@@ -105,18 +105,11 @@ private:
// This node represents a view namespace if and only if 'children' is nonempty and 'collator' is
// set.
struct Node {
- Node() = default;
- Node(const Node& other)
- : ns(other.ns), children(other.children), parents(other.parents), size(other.size) {
- if (other.collator) {
- collator = CollatorInterface::cloneCollator(other.collator.get());
- }
- }
-
/**
* Returns true if this node represents a view.
*/
bool isView() const {
+ invariant(children.empty() == !static_cast<bool>(collator));
return !children.empty();
}
@@ -131,10 +124,10 @@ private:
// Represents the views that depend on this namespace.
stdx::unordered_set<uint64_t> parents;
- // When set to nullptr, the view either has the binary collation or this namespace is not a
- // view and we don't care about its collator. Verify if view with isView. ViewGraph owns the
- // collator in order to keep pointer alive after insertion.
- std::unique_ptr<const CollatorInterface> collator;
+ // When set, this is an unowned pointer to the view's collation, or nullptr if the view has
+ // the binary collation. When not set, this namespace is not a view and we don't care about
+ // its collator.
+ boost::optional<const CollatorInterface*> collator;
// The size of this view's "pipeline", in bytes.
int size = 0;