@@ -302,6 +302,23 @@ bool mrSupportsWriteConcern(const BSONObj& cmd) {
302302
303303std::unique_ptr<Pipeline, PipelineDeleter> translateFromMR (
304304 MapReduce parsedMr, boost::intrusive_ptr<ExpressionContext> expCtx) {
305+ // Verify that source and output collections are different.
306+ // Note that $out allows for the source and the destination to match, so only reject
307+ // in the case that the out option is being converted to a $merge.
308+ auto & inNss = parsedMr.getNamespace ();
309+ auto outNss = NamespaceString{parsedMr.getOutOptions ().getDatabaseName ()
310+ ? *parsedMr.getOutOptions ().getDatabaseName ()
311+ : parsedMr.getNamespace ().db (),
312+ parsedMr.getOutOptions ().getCollectionName ()};
313+
314+ auto outType = parsedMr.getOutOptions ().getOutputType ();
315+ if (outType == OutputType::Merge || outType == OutputType::Reduce) {
316+ uassert (ErrorCodes::InvalidOptions,
317+ " Source collection cannot be the same as destination collection in MapReduce when "
318+ " using merge or "
319+ " reduce actions" ,
320+ inNss != outNss);
321+ }
305322
306323 // TODO: It would be good to figure out what kind of errors this would produce in the Status.
307324 // It would be better not to produce something incomprehensible out of an internal translation.
@@ -318,12 +335,9 @@ std::unique_ptr<Pipeline, PipelineDeleter> translateFromMR(
318335 return translateFinalize (expCtx, parsedMr.getFinalize ()->getCode ());
319336 }),
320337 translateOut (expCtx,
321- parsedMr. getOutOptions (). getOutputType () ,
338+ outType ,
322339 parsedMr.getNamespace ().db (),
323- NamespaceString{parsedMr.getOutOptions ().getDatabaseName ()
324- ? *parsedMr.getOutOptions ().getDatabaseName ()
325- : parsedMr.getNamespace ().db (),
326- parsedMr.getOutOptions ().getCollectionName ()},
340+ std::move (outNss),
327341 parsedMr.getReduce ().getCode ())),
328342 expCtx));
329343}
0 commit comments