Skip to content

Commit 7264dd0

Browse files
mtandreievergreen
authored andcommitted
SERVER-43267 Fail MapReduce when source collection is the same as destination collection
1 parent 13e2014 commit 7264dd0

File tree

2 files changed

+51
-5
lines changed

2 files changed

+51
-5
lines changed

src/mongo/db/commands/map_reduce_agg_test.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,15 @@
4646
#include "mongo/db/pipeline/expression_context_for_test.h"
4747
#include "mongo/unittest/unittest.h"
4848

49+
#define ASSERT_DOES_NOT_THROW(EXPRESSION) \
50+
try { \
51+
EXPRESSION; \
52+
} catch (const AssertionException& e) { \
53+
str::stream err; \
54+
err << "Threw an exception incorrectly: " << e.toString(); \
55+
::mongo::unittest::TestAssertionFailure(__FILE__, __LINE__, err).stream(); \
56+
}
57+
4958
namespace mongo {
5059
namespace {
5160

@@ -212,5 +221,28 @@ TEST(MapReduceAggTest, testOutSameCollection) {
212221
ASSERT(typeid(DocumentSourceOut) == typeid(**iter));
213222
}
214223

224+
TEST(MapReduceAggTest, testSourceDestinationCollectionsEqualMergeFail) {
225+
auto nss = NamespaceString{"db", "coll"};
226+
auto mr = MapReduce{
227+
nss,
228+
MapReduceJavascriptCode{mapJavascript.toString()},
229+
MapReduceJavascriptCode{reduceJavascript.toString()},
230+
MapReduceOutOptions{boost::make_optional("db"s), "coll", OutputType::Merge, false}};
231+
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest(nss));
232+
ASSERT_THROWS_CODE(
233+
map_reduce_common::translateFromMR(mr, expCtx), DBException, ErrorCodes::InvalidOptions);
234+
}
235+
236+
TEST(MapReduceAggTest, testSourceDestinationCollectionsNotEqualMergeDoesNotFail) {
237+
auto nss = NamespaceString{"db", "coll"};
238+
auto mr = MapReduce{
239+
nss,
240+
MapReduceJavascriptCode{mapJavascript.toString()},
241+
MapReduceJavascriptCode{reduceJavascript.toString()},
242+
MapReduceOutOptions{boost::make_optional("db2"s), "coll", OutputType::Merge, false}};
243+
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest(nss));
244+
ASSERT_DOES_NOT_THROW(map_reduce_common::translateFromMR(mr, expCtx));
245+
}
246+
215247
} // namespace
216248
} // namespace mongo

src/mongo/db/commands/mr_common.cpp

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,23 @@ bool mrSupportsWriteConcern(const BSONObj& cmd) {
302302

303303
std::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

Comments
 (0)