diff options
author | Kyle Suarez <kyle.suarez@mongodb.com> | 2016-09-06 15:31:11 -0400 |
---|---|---|
committer | Kyle Suarez <kyle.suarez@mongodb.com> | 2016-09-06 15:40:31 -0400 |
commit | f515afc5dd533dc2ddc3d80697d9ff96709f9f0d (patch) | |
tree | 6ae39de400121805c436a2fdfb00c95d4e00853d /src/mongo/db/commands | |
parent | 8081859b784662566c6efa3f06a2e6ffd4912afa (diff) | |
download | mongo-f515afc5dd533dc2ddc3d80697d9ff96709f9f0d.tar.gz |
SERVER-25186 support a default collation for views
Users may specify a default collation when creating a view. Operations involving
a view cannot override the view's default collation.
Diffstat (limited to 'src/mongo/db/commands')
-rw-r--r-- | src/mongo/db/commands/list_collections.cpp | 11 | ||||
-rw-r--r-- | src/mongo/db/commands/pipeline_command.cpp | 58 |
2 files changed, 67 insertions, 2 deletions
diff --git a/src/mongo/db/commands/list_collections.cpp b/src/mongo/db/commands/list_collections.cpp index d3dce59c032..65eab818051 100644 --- a/src/mongo/db/commands/list_collections.cpp +++ b/src/mongo/db/commands/list_collections.cpp @@ -128,8 +128,15 @@ BSONObj buildViewBson(const ViewDefinition& view) { BSONObjBuilder b; b.append("name", view.name().coll()); b.append("type", "view"); - BSONObj options = BSON("viewOn" << view.viewOn().coll() << "pipeline" << view.pipeline()); - b.append("options", options); + + BSONObjBuilder optionsBuilder(b.subobjStart("options")); + optionsBuilder.append("viewOn", view.viewOn().coll()); + optionsBuilder.append("pipeline", view.pipeline()); + if (view.defaultCollator()) { + optionsBuilder.append("collation", view.defaultCollator()->getSpec().toBSON()); + } + optionsBuilder.doneFast(); + BSONObj info = BSON("readOnly" << true); b.append("info", info); return b.obj(); diff --git a/src/mongo/db/commands/pipeline_command.cpp b/src/mongo/db/commands/pipeline_command.cpp index 69f8a1ecccb..d797b9ee714 100644 --- a/src/mongo/db/commands/pipeline_command.cpp +++ b/src/mongo/db/commands/pipeline_command.cpp @@ -53,6 +53,7 @@ #include "mongo/db/pipeline/expression_context.h" #include "mongo/db/pipeline/pipeline.h" #include "mongo/db/pipeline/pipeline_d.h" +#include "mongo/db/query/collation/collator_factory_interface.h" #include "mongo/db/query/cursor_response.h" #include "mongo/db/query/find_common.h" #include "mongo/db/query/get_executor.h" @@ -268,6 +269,35 @@ boost::intrusive_ptr<Pipeline> reparsePipeline( return reparsedPipeline.getValue(); } +/** + * Returns Status::OK if each view namespace in 'pipeline' has a default collator equivalent to + * 'collator'. Otherwise, returns ErrorCodes::OptionNotSupportedOnView. + */ +Status collatorCompatibleWithPipeline(OperationContext* txn, + Database* db, + const CollatorInterface* collator, + const intrusive_ptr<Pipeline> pipeline) { + if (!db || !pipeline) { + return Status::OK(); + } + for (auto&& potentialViewNs : pipeline->getInvolvedCollections()) { + if (db->getCollection(potentialViewNs.ns())) { + continue; + } + + auto view = db->getViewCatalog()->lookup(txn, potentialViewNs.ns()); + if (!view) { + continue; + } + if (!CollatorInterface::collatorsMatch(view->defaultCollator(), collator)) { + return {ErrorCodes::OptionNotSupportedOnView, + str::stream() << "Cannot override default collation of view " + << potentialViewNs.ns()}; + } + } + return Status::OK(); +} + class PipelineCommand : public Command { public: PipelineCommand() @@ -365,6 +395,23 @@ public: // prohibit yielding.) auto view = ctx.getView(); if (view && !startsWithCollStats()) { + // Check that the default collation of 'view' is compatible with the + // operation's collation. The check is skipped if the 'request' has the empty + // collation, which means that no collation was specified. + if (!request.getCollation().isEmpty()) { + auto operationCollator = CollatorFactoryInterface::get(txn->getServiceContext()) + ->makeFromBSON(request.getCollation()); + if (!operationCollator.isOK()) { + return appendCommandStatus(result, operationCollator.getStatus()); + } + if (!CollatorInterface::collatorsMatch(view->defaultCollator(), + operationCollator.getValue().get())) { + return appendCommandStatus(result, + {ErrorCodes::OptionNotSupportedOnView, + "Cannot override a view's default collation"}); + } + } + auto viewDefinition = ViewShardingCheck::getResolvedViewIfSharded(txn, ctx.getDb(), view); if (!viewDefinition.isOK()) { @@ -394,6 +441,9 @@ public: if (!newRequest.isOK()) { return appendCommandStatus(result, newRequest.getStatus()); } + newRequest.getValue().setCollation(view->defaultCollator() + ? view->defaultCollator()->getSpec().toBSON() + : CollationSpec::kSimpleSpec); bool status = runParsed( txn, origNss, newRequest.getValue(), newCmd.getValue(), errmsg, result); @@ -414,6 +464,14 @@ public: expCtx->setCollator(collection->getDefaultCollator()->clone()); } + // Check that the view's collation matches the collation of any views involved + // in the pipeline. + auto pipelineCollationStatus = + collatorCompatibleWithPipeline(txn, ctx.getDb(), expCtx->getCollator(), pipeline); + if (!pipelineCollationStatus.isOK()) { + return appendCommandStatus(result, pipelineCollationStatus); + } + // Propagate the ExpressionContext throughout all of the pipeline's stages and // expressions. pipeline->injectExpressionContext(expCtx); |