summaryrefslogtreecommitdiff
path: root/src/mongo/db/commands
diff options
context:
space:
mode:
authorKyle Suarez <kyle.suarez@mongodb.com>2016-09-06 15:31:11 -0400
committerKyle Suarez <kyle.suarez@mongodb.com>2016-09-06 15:40:31 -0400
commitf515afc5dd533dc2ddc3d80697d9ff96709f9f0d (patch)
tree6ae39de400121805c436a2fdfb00c95d4e00853d /src/mongo/db/commands
parent8081859b784662566c6efa3f06a2e6ffd4912afa (diff)
downloadmongo-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.cpp11
-rw-r--r--src/mongo/db/commands/pipeline_command.cpp58
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);