diff options
| author | Lucas de Castro Borges <lucas@gnuabordo.com.br> | 2025-02-11 15:07:35 -0300 |
|---|---|---|
| committer | Lucas de Castro Borges <lucas@gnuabordo.com.br> | 2025-02-11 15:07:35 -0300 |
| commit | 4cb8841196d0625dfa3825aa326f071cd27c7b8b (patch) | |
| tree | 1682a647d4463397c119183369ae6f750d5fdcff /src/mongo/db/views | |
| parent | aa03c6362cbaa767638e6eed9b031d86dd2643d1 (diff) | |
| parent | 8f0827553e09872941945a093b647a4211a9db7f (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/SConscript | 1 | ||||
| -rw-r--r-- | src/mongo/db/views/durable_view_catalog.cpp | 121 | ||||
| -rw-r--r-- | src/mongo/db/views/durable_view_catalog.h | 8 | ||||
| -rw-r--r-- | src/mongo/db/views/resolved_view.cpp | 119 | ||||
| -rw-r--r-- | src/mongo/db/views/resolved_view.h | 23 | ||||
| -rw-r--r-- | src/mongo/db/views/view_catalog_helpers.cpp | 5 | ||||
| -rw-r--r-- | src/mongo/db/views/view_graph.cpp | 5 | ||||
| -rw-r--r-- | src/mongo/db/views/view_graph.h | 17 |
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; |
