summaryrefslogtreecommitdiff
path: root/src/mongo/db/ops
diff options
context:
space:
mode:
authorBernard Gorman <bernard.gorman@mongodb.com>2019-11-14 21:59:35 +0000
committerevergreen <evergreen@mongodb.com>2019-11-14 21:59:35 +0000
commit23e55cb3d041236f399f7095df31cd3e3da491cc (patch)
tree25bc309af51bc66dbd46922b0cf7560b3351478a /src/mongo/db/ops
parentcdc44d95e169da75093f25c324aa9670e72743e8 (diff)
downloadmongo-23e55cb3d041236f399f7095df31cd3e3da491cc.tar.gz
SERVER-43860 Always upsert exact source document for pipeline-insert $merge
Diffstat (limited to 'src/mongo/db/ops')
-rw-r--r--src/mongo/db/ops/parsed_update.cpp17
-rw-r--r--src/mongo/db/ops/update_request.h39
-rw-r--r--src/mongo/db/ops/write_ops.idl5
-rw-r--r--src/mongo/db/ops/write_ops_exec.cpp1
4 files changed, 44 insertions, 18 deletions
diff --git a/src/mongo/db/ops/parsed_update.cpp b/src/mongo/db/ops/parsed_update.cpp
index 073ea2d4865..6c68a621d82 100644
--- a/src/mongo/db/ops/parsed_update.cpp
+++ b/src/mongo/db/ops/parsed_update.cpp
@@ -32,6 +32,7 @@
#include "mongo/db/ops/parsed_update.h"
#include "mongo/db/ops/update_request.h"
+#include "mongo/db/ops/write_ops_gen.h"
#include "mongo/db/query/canonical_query.h"
#include "mongo/db/query/collation/collator_factory_interface.h"
#include "mongo/db/query/query_planner_common.h"
@@ -53,6 +54,22 @@ Status ParsedUpdate::parseRequest() {
// of a document during a multi-update.
invariant(!(_request->shouldReturnAnyDocs() && _request->isMulti()));
+ // It is invalid to specify 'upsertSupplied:true' for a non-upsert operation, or if no upsert
+ // document was supplied with the request.
+ if (_request->shouldUpsertSuppliedDocument()) {
+ uassert(ErrorCodes::FailedToParse,
+ str::stream() << "cannot specify '"
+ << write_ops::UpdateOpEntry::kUpsertSuppliedFieldName
+ << ": true' for a non-upsert operation",
+ _request->isUpsert());
+ const auto& constants = _request->getUpdateConstants();
+ uassert(ErrorCodes::FailedToParse,
+ str::stream() << "the parameter '"
+ << write_ops::UpdateOpEntry::kUpsertSuppliedFieldName
+ << "' is set to 'true', but no document was supplied",
+ constants && (*constants)["new"_sd].type() == BSONType::Object);
+ }
+
// It is invalid to request that a ProjectionStage be applied to the UpdateStage if the
// UpdateStage would not return any document.
invariant(_request->getProj().isEmpty() || _request->shouldReturnAnyDocs());
diff --git a/src/mongo/db/ops/update_request.h b/src/mongo/db/ops/update_request.h
index f946302c6b9..5fa0a561b7e 100644
--- a/src/mongo/db/ops/update_request.h
+++ b/src/mongo/db/ops/update_request.h
@@ -56,16 +56,7 @@ public:
RETURN_NEW
};
- inline UpdateRequest(const NamespaceString& nsString)
- : _nsString(nsString),
- _god(false),
- _upsert(false),
- _multi(false),
- _fromMigration(false),
- _fromOplogApplication(false),
- _isExplain(false),
- _returnDocs(ReturnDocOption::RETURN_NONE),
- _yieldPolicy(PlanExecutor::NO_YIELD) {}
+ inline UpdateRequest(const NamespaceString& nsString) : _nsString(nsString) {}
const NamespaceString& getNamespaceString() const {
return _nsString;
@@ -154,6 +145,14 @@ public:
return _upsert;
}
+ inline void setUpsertSuppliedDocument(bool value = true) {
+ _upsertSuppliedDocument = value;
+ }
+
+ bool shouldUpsertSuppliedDocument() const {
+ return _upsertSuppliedDocument;
+ }
+
inline void setMulti(bool value = true) {
_multi = value;
}
@@ -306,22 +305,26 @@ private:
// God bypasses _id checking and index generation. It is only used on behalf of system
// updates, never user updates.
- bool _god;
+ bool _god = false;
// True if this should insert if no matching document is found.
- bool _upsert;
+ bool _upsert = false;
+
+ // True if this upsert operation should insert the document supplied as 'c.new' if the query
+ // does not match any documents.
+ bool _upsertSuppliedDocument = false;
// True if this update is allowed to affect more than one document.
- bool _multi;
+ bool _multi = false;
// True if this update is on behalf of a chunk migration.
- bool _fromMigration;
+ bool _fromMigration = false;
// True if this update was triggered by the application of an oplog entry.
- bool _fromOplogApplication;
+ bool _fromOplogApplication = false;
// Whether or not we are requesting an explained update. Explained updates are read-only.
- bool _isExplain;
+ bool _isExplain = false;
// Specifies which version of the documents to return, if any.
//
@@ -335,10 +338,10 @@ private:
//
// This allows findAndModify to execute an update and retrieve the resulting document
// without another query before or after the update.
- ReturnDocOption _returnDocs;
+ ReturnDocOption _returnDocs = ReturnDocOption::RETURN_NONE;
// Whether or not the update should yield. Defaults to NO_YIELD.
- PlanExecutor::YieldPolicy _yieldPolicy;
+ PlanExecutor::YieldPolicy _yieldPolicy = PlanExecutor::NO_YIELD;
};
} // namespace mongo
diff --git a/src/mongo/db/ops/write_ops.idl b/src/mongo/db/ops/write_ops.idl
index 9409aa73041..42f86b1f481 100644
--- a/src/mongo/db/ops/write_ops.idl
+++ b/src/mongo/db/ops/write_ops.idl
@@ -127,6 +127,11 @@ structs:
operation inserts only a single document."
type: bool
default: false
+ upsertSupplied:
+ description: "Only applicable when upsert is true. If set, and if no documents match
+ the query, the update subsystem will insert the document supplied as
+ 'c.new' rather than generating a new document from the update spec."
+ type: optionalBool
collation:
description: "Specifies the collation to use for the operation."
type: object
diff --git a/src/mongo/db/ops/write_ops_exec.cpp b/src/mongo/db/ops/write_ops_exec.cpp
index 38da11a7d0e..3b1c09b6a62 100644
--- a/src/mongo/db/ops/write_ops_exec.cpp
+++ b/src/mongo/db/ops/write_ops_exec.cpp
@@ -724,6 +724,7 @@ static SingleWriteResult performSingleUpdateOpWithDupKeyRetry(OperationContext*
request.setArrayFilters(write_ops::arrayFiltersOf(op));
request.setMulti(op.getMulti());
request.setUpsert(op.getUpsert());
+ request.setUpsertSuppliedDocument(op.getUpsertSupplied());
request.setHint(op.getHint());
request.setYieldPolicy(opCtx->inMultiDocumentTransaction() ? PlanExecutor::INTERRUPT_ONLY