summaryrefslogtreecommitdiff
path: root/jstests/aggregation
diff options
context:
space:
mode:
authorDavid Storch <david.storch@10gen.com>2018-12-07 10:05:44 -0500
committerDavid Storch <david.storch@10gen.com>2018-12-07 16:14:56 -0500
commit7000ad6f39fe8a6cc82935bb5a81a0a278b98661 (patch)
treea462717033e6b07e49a86c2824c82e2165e65796 /jstests/aggregation
parent67e98183b1d80bd12c6fd815fffe4e51619e0156 (diff)
downloadmongo-7000ad6f39fe8a6cc82935bb5a81a0a278b98661.tar.gz
SERVER-38063 Fail cleanly on execution-level explain of $out pipeline.
Diffstat (limited to 'jstests/aggregation')
-rw-r--r--jstests/aggregation/sources/explain_out.js71
1 files changed, 71 insertions, 0 deletions
diff --git a/jstests/aggregation/sources/explain_out.js b/jstests/aggregation/sources/explain_out.js
new file mode 100644
index 00000000000..5d49ffcec05
--- /dev/null
+++ b/jstests/aggregation/sources/explain_out.js
@@ -0,0 +1,71 @@
+/**
+ * Test aggregation explain of $out pipelines.
+ *
+ * The 'replaceCollection' $out mode is not allowed with a sharded output collection. Explain of
+ * $out does not accept writeConcern.
+ * @tags: [assumes_unsharded_collection, assumes_write_concern_unchanged]
+ */
+(function() {
+ "use strict";
+
+ load("jstests/libs/fixture_helpers.js"); // For FixtureHelpers.isMongos().
+ load("jstests/libs/analyze_plan.js"); // For getAggPlanStage().
+ load("jstests/aggregation/extras/out_helpers.js"); // For withEachOutMode().
+
+ // Mongos currently uses its own error code if any shard's explain fails.
+ const kErrorCode = FixtureHelpers.isMongos(db) ? 17403 : 51028;
+
+ let sourceColl = db.explain_out_source;
+ let targetColl = db.explain_out_target;
+ sourceColl.drop();
+ targetColl.drop();
+
+ assert.writeOK(sourceColl.insert({_id: 1}));
+
+ function assertQueryPlannerExplainSucceeds(outStage) {
+ let explain = sourceColl.explain("queryPlanner").aggregate([outStage]);
+ let outExplain = getAggPlanStage(explain, "$out");
+ assert.neq(outExplain, null, explain);
+ assert.eq(targetColl.find().itcount(), 0, explain);
+ return outExplain.$out;
+ }
+
+ // Test each out mode with 'queryPlanner' explain verbosity;
+ withEachOutMode(function(outMode) {
+ const outStage = {$out: {to: targetColl.getName(), mode: outMode}};
+ const explain = sourceColl.explain("queryPlanner").aggregate([outStage]);
+ const outExplain = getAggPlanStage(explain, "$out");
+ assert.neq(outExplain, null, explain);
+ assert(outExplain.hasOwnProperty("$out"), explain);
+ assert.eq(outExplain.$out.mode, outMode, outExplain);
+ assert.eq(outExplain.$out.uniqueKey, {_id: 1}, outExplain);
+ assert.eq(targetColl.find().itcount(), 0, explain);
+ });
+
+ function assertExecutionExplainFails(outStage, verbosity) {
+ assert.commandFailedWithCode(db.runCommand({
+ explain: {aggregate: sourceColl.getName(), pipeline: [outStage], cursor: {}},
+ verbosity: verbosity
+ }),
+ kErrorCode);
+ assert.eq(targetColl.find().itcount(), 0);
+ }
+
+ // Test that 'executionStats' and 'allPlansExec' level explain fail with each $out mode. These
+ // explain modes must fail, since they would attempt to do writes. Explain must always be
+ // read-only (including explain of update and delete, which describe what writes they _would_ do
+ // if exected for real).
+ withEachOutMode(function(outMode) {
+ const outStage = {$out: {to: targetColl.getName(), mode: outMode}};
+ assertExecutionExplainFails(outStage, "executionStats");
+ assertExecutionExplainFails(outStage, "allPlansExecution");
+ });
+
+ // Execution explain should fail even if the source collection does not exist.
+ sourceColl.drop();
+ withEachOutMode(function(outMode) {
+ const outStage = {$out: {to: targetColl.getName(), mode: outMode}};
+ assertExecutionExplainFails(outStage, "executionStats");
+ assertExecutionExplainFails(outStage, "allPlansExecution");
+ });
+}());