Files
mongo/jstests/sharding/shard_key_immutable.js

884 lines
33 KiB
JavaScript

/**
* Shard key invariant:
*
* A document must be created with a full non-array-or-regex shard key, and the value of that shard
* key can never change.
*
* To enforce this invariant, we have the following mongos rule:
*
* - Upserts must always contain the full shard key and must only be targeted* to the applicable shard.
*
* and the following mongod rules:
*
* - Upserted shard key values must not be arrays (or regexes).
* - If a shard key value is present in the update query, upserts must only insert documents which
* match this value.
* - Updates must not modify shard keys.
*
* *Updates are targeted by the update query if $op-style, or the update document if replacement-style.
*
* NOTE: The above is enough to ensure that shard keys do not change. It is not enough to ensure
* uniqueness of an upserted document based on the upsert query. This is necessary due to the save()
* style operation:
* db.coll.update({ _id : xxx }, { _id : xxx, shard : xxx, key : xxx, other : xxx }, { upsert : true })
*
* TODO: Minimize the impact of this hole by disallowing anything but save-style upserts of this form.
* Save-style upserts of this form are not safe (duplicate _ids can be created) but the user is
* explicitly responsible for this for the _id field.
*
* In addition, there is an rule where non-multi updates can only affect 0 or 1 documents.
*
* To enforce this, we have the following mongos rule:
*
* - Non-multi updates must be targeted based on an exact _id query or the full shard key.
*
* Test setup:
* - replacement style updates have the multiUpdate flag set to false.
* - $ op updates have multiUpdate flag set to true.
*/
var st = new ShardingTest({ shards: 2 });
st.adminCommand({ enablesharding: "test" });
st.ensurePrimaryShard('test', 'shard0001');
st.adminCommand({ shardcollection: "test.col0", key: { a: 1, b: 1 }});
st.adminCommand({ shardcollection: "test.col1", key: { 'x.a': 1 }});
var db = st.s.getDB('test');
var compoundColl = db.getCollection('col0');
var dotColl = db.getCollection('col1');
//
// Empty query update
//
compoundColl.remove({}, false);
compoundColl.insert({ a: 100, b: 100 });
assert.writeError(compoundColl.update({}, { a: 1 }, false));
var doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc));
doc = compoundColl.findOne();
compoundColl.remove({}, false);
compoundColl.insert({ a: 100, b: 100 });
assert.writeError(compoundColl.update({}, { a: 1, b: 1 }, false));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc));
compoundColl.remove({}, false);
compoundColl.insert({ a: 100, b: 100 });
assert.writeOK(compoundColl.update({}, { a: 100, b: 100 }, false));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc));
// Cannot modify _id!
compoundColl.remove({}, false);
compoundColl.insert({ a: 100, b: 100 });
assert.writeError(compoundColl.update({}, { a: 100, b: 100, _id: 1 }, false));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc));
compoundColl.remove({}, false);
compoundColl.insert({ a: 100, b: 100 });
assert.writeError(compoundColl.update({}, { $set: { a: 1, b: 1 }}, false, true));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc));
compoundColl.remove({}, false);
compoundColl.insert({ a: 100, b: 100 });
assert.writeOK(compoundColl.update({}, { $set: { a: 100, b: 100 }}, false, true));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc));
// Cannot modify _id
compoundColl.remove({}, false);
compoundColl.insert({ a: 100, b: 100 });
assert.writeError(compoundColl.update({}, { $set: { a: 1, b: 1, _id: 1 }}, false, true));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc));
compoundColl.remove({}, false);
compoundColl.insert({ a: 100, b: 100 });
assert.writeOK(compoundColl.update({}, { $set: { c: 1 }}, false, true));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100, c: 1 }), 'doc did not change: ' + tojson(doc));
//
// Empty query upsert
//
compoundColl.remove({}, false);
assert.writeError(compoundColl.update({}, { a: 1 }, true));
doc = compoundColl.findOne();
assert(doc == null, 'doc upserted: ' + tojson(doc));
compoundColl.remove({}, false);
assert.writeOK(compoundColl.update({}, { a: 1, b: 1 }, true));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 1, b: 1 }), 'doc not upserted properly: ' + tojson(doc));
// Cannot modify _id!
compoundColl.remove({}, false);
assert.writeOK(compoundColl.update({}, { a: 1, b: 1, _id: 1 }, true));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 1, b: 1 }), 'doc not upserted properly: ' + tojson(doc));
compoundColl.remove({}, false);
assert.writeError(compoundColl.update({}, { $set: { a: 1 }}, true, true));
doc = compoundColl.findOne();
assert(doc == null, 'doc upserted: ' + tojson(doc));
compoundColl.remove({}, false);
assert.writeError(compoundColl.update({}, { $set: { a: 1, b: 1 }}, true, true));
doc = compoundColl.findOne();
assert(doc == null, 'doc upserted: ' + tojson(doc));
// Cannot modify _id!
compoundColl.remove({}, false);
assert.writeError(compoundColl.update({}, { $set: { a: 1, b: 1, _id: 1 }}, true, true));
doc = compoundColl.findOne();
assert(doc == null, 'doc upserted: ' + tojson(doc));
compoundColl.remove({}, false);
assert.writeError(compoundColl.update({}, { $set: { c: 1 }}, true, true));
doc = compoundColl.findOne();
assert(doc == null, 'doc upserted: ' + tojson(doc));
//
// Partial skey query update
//
compoundColl.remove({}, false);
compoundColl.insert({ a: 100, b: 100 });
assert.writeError(compoundColl.update({ a: 100 }, { a: 100 }, false));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc));
compoundColl.remove({}, false);
compoundColl.insert({ a: 100, b: 100 });
assert.writeError(compoundColl.update({ a: 100 }, { a: 2 }, false));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc));
compoundColl.remove({}, false);
compoundColl.insert({ a: 100, b: 100 });
assert.writeError(compoundColl.update({ a: 100 }, { a: 100, b: 1 }, false));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc));
// Inspecting query and update alone is not enough to tell whether a shard key will change.
compoundColl.remove({}, false);
compoundColl.insert({ a: 100, b: 100 });
assert.writeOK(compoundColl.update({ a: 100 }, { a: 100, b: 100 }, false));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc));
// Cannot modify _id!
compoundColl.remove({}, false);
compoundColl.insert({ a: 100, b: 100 });
assert.writeError(compoundColl.update({ a: 100 }, { a: 100, b: 100, _id: 1 }, false));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc));
compoundColl.remove({}, false);
compoundColl.insert({ a: 100, b: 100 });
assert.writeOK(compoundColl.update({ a: 100 }, { $set: { a: 100 }}, false, true));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc));
compoundColl.remove({}, false);
compoundColl.insert({ a: 100, b: 100 });
assert.writeError(compoundColl.update({ a: 100 }, { $set: { b: 200 }}, false, true));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc));
// Inspecting query and update alone is not enough to tell whether a shard key will change.
compoundColl.remove({}, false);
compoundColl.insert({ a: 100, b: 100 });
assert.writeOK(compoundColl.update({ a: 100 }, { $set: { b: 100 }}, false, true));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc));
compoundColl.remove({}, false);
compoundColl.insert({ a: 100, b: 100 });
assert.writeError(compoundColl.update({ a: 100 }, { $set: { a: 100, b: 200 }}, false, true));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc));
// Inspecting query and update alone is not enough to tell whether a shard key will change.
compoundColl.remove({}, false);
compoundColl.insert({ a: 100, b: 100 });
assert.writeOK(compoundColl.update({ a: 100 }, { $set: { a: 100, b: 100 }}, false, true));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc));
// Cannot modify _id!
compoundColl.remove({}, false);
compoundColl.insert({ a: 100, b: 100 });
assert.writeError(compoundColl.update({ a: 100 }, { $set: { a: 100, b: 100, _id: 1 }}, false, true));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc));
compoundColl.remove({}, false);
compoundColl.insert({ a: 100, b: 100 });
assert.writeOK(compoundColl.update({ a: 100 }, { $set: { c: 1 }}, false, true));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100, c: 1 }), 'doc did not change: ' + tojson(doc));
compoundColl.remove({}, false);
compoundColl.insert({ a: 100, b: 100 });
assert.writeOK(compoundColl.update({ a: 100 }, { $rename: { c: 'a' }}, false, true));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc));
//
// Partial skey query upsert
//
compoundColl.remove({}, false);
assert.writeError(compoundColl.update({ a: 100 }, { a: 100 }, true));
doc = compoundColl.findOne();
assert(doc == null, 'doc upserted: ' + tojson(doc));
compoundColl.remove({}, false);
assert.writeError(compoundColl.update({ a: 100 }, { a: 2 }, true));
doc = compoundColl.findOne();
assert(doc == null, 'doc upserted: ' + tojson(doc));
compoundColl.remove({}, false);
assert.writeError(compoundColl.update({ a: 100 }, { a: 1, b: 1 }, true));
doc = compoundColl.findOne();
assert(doc == null, 'doc upserted: ' + tojson(doc));
// Cannot modify _id!
compoundColl.remove({}, false);
assert.writeError(compoundColl.update({ a: 100 }, { a: 1, b: 1, _id: 1 }, true));
doc = compoundColl.findOne();
assert(doc == null, 'doc upserted: ' + tojson(doc));
compoundColl.remove({}, false);
assert.writeError(compoundColl.update({ a: 100 }, { $set: { a: 1 }}, true, true));
doc = compoundColl.findOne();
assert(doc == null, 'doc upserted: ' + tojson(doc));
compoundColl.remove({}, false);
assert.writeError(compoundColl.update({ a: 100 }, { $set: { b: 1 }}, true, true));
doc = compoundColl.findOne();
assert(doc == null, 'doc upserted: ' + tojson(doc));
compoundColl.remove({}, false);
assert.writeError(compoundColl.update({ a: 100 }, { $set: { a: 100, b: 1 }}, true, true));
doc = compoundColl.findOne();
assert(doc == null, 'doc upserted: ' + tojson(doc));
// Cannot modify _id!
compoundColl.remove({}, false);
assert.writeError(compoundColl.update({ a: 100 }, { $set: { a: 100, b: 1, _id: 1 }}, true, true));
doc = compoundColl.findOne();
assert(doc == null, 'doc upserted: ' + tojson(doc));
compoundColl.remove({}, false);
assert.writeError(compoundColl.update({ a: 100 }, { $set: { c: 1 }}, true, true));
doc = compoundColl.findOne();
assert(doc == null, 'doc upserted: ' + tojson(doc));
compoundColl.remove({}, false);
assert.writeError(compoundColl.update({ a: 100 }, { $rename: { c: 'a' }}, true, true));
doc = compoundColl.findOne();
assert(doc == null, 'doc upserted: ' + tojson(doc));
//
// Not prefix of skey query update
//
compoundColl.remove({}, false);
compoundColl.insert({ a: 100, b: 100 });
assert.writeError(compoundColl.update({ b: 100 }, { b: 100 }, false));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc));
compoundColl.remove({}, false);
compoundColl.insert({ a: 100, b: 100 });
assert.writeError(compoundColl.update({ b: 100 }, { b: 2 }, false));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc));
compoundColl.remove({}, false);
compoundColl.insert({ a: 100, b: 100 });
assert.writeError(compoundColl.update({ b: 100 }, { a: 1 }, false));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc));
// Inspecting query and update alone is not enough to tell whether a shard key will change.
compoundColl.remove({}, false);
compoundColl.insert({ a: 100, b: 100 });
assert.writeError(compoundColl.update({ b: 100 }, { a: 100 }, false));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc));
compoundColl.remove({}, false);
compoundColl.insert({ a: 100, b: 100 });
assert.writeError(compoundColl.update({ b: 100 }, { a: 1, b: 100 }, false));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc));
// Inspecting query and update alone is not enough to tell whether a shard key will change.
compoundColl.remove({}, false);
compoundColl.insert({ a: 100, b: 100 });
assert.writeOK(compoundColl.update({ b: 100 }, { a: 100, b: 100 }, false));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc));
// Cannot modify _id!
compoundColl.remove({}, false);
compoundColl.insert({ a: 100, b: 100 });
assert.writeError(compoundColl.update({ b: 100 }, { a: 1, b: 1, _id: 1 }, false));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc));
compoundColl.remove({}, false);
compoundColl.insert({ a: 100, b: 100 });
assert.writeError(compoundColl.update({ b: 100 }, { $set: { b: 1 }}, false, true));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc));
compoundColl.remove({}, false);
compoundColl.insert({ a: 100, b: 100 });
assert.writeError(compoundColl.update({ b: 100 }, { $set: { a: 1 }}, false, true));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc));
compoundColl.remove({}, false);
compoundColl.insert({ a: 100, b: 100 });
assert.writeOK(compoundColl.update({ b: 100 }, { $set: { a: 100 }}, false, true));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc));
compoundColl.remove({}, false);
compoundColl.insert({ a: 100, b: 100 });
assert.writeError(compoundColl.update({ b: 100 }, { $set: { a: 1, b: 100 }}, false, true));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc));
// Inspecting query and update alone is not enough to tell whether a shard key will change.
compoundColl.remove({}, false);
compoundColl.insert({ a: 100, b: 100 });
assert.writeOK(compoundColl.update({ b: 100 }, { $set: { a: 100, b: 100 }}, false, true));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc));
// Cannot modify _id!
compoundColl.remove({}, false);
compoundColl.insert({ a: 100, b: 100 });
assert.writeError(compoundColl.update({ b: 100 }, { $set: { a: 100, b: 100, _id: 1 }}, false, true));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc));
compoundColl.remove({}, false);
compoundColl.insert({ a: 100, b: 100 });
assert.writeOK(compoundColl.update({ b: 100 }, { $set: { c: 1 }}, false, true));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100, c: 1 }), 'doc did not change: ' + tojson(doc));
//
// Not prefix of skey query upsert
//
compoundColl.remove({}, false);
assert.writeError(compoundColl.update({ b: 100 }, { b: 100 }, true));
doc = compoundColl.findOne();
assert(doc == null, 'doc upserted: ' + tojson(doc));
compoundColl.remove({}, false);
assert.writeError(compoundColl.update({ b: 100 }, { b: 2 }, true));
doc = compoundColl.findOne();
assert(doc == null, 'doc upserted: ' + tojson(doc));
compoundColl.remove({}, false);
assert.writeError(compoundColl.update({ b: 100 }, { a: 1 }, true));
doc = compoundColl.findOne();
assert(doc == null, 'doc upserted: ' + tojson(doc));
compoundColl.remove({}, false);
assert.writeError(compoundColl.update({ b: 100 }, { a: 1, b: 1 }, true));
doc = compoundColl.findOne();
assert(doc == null, 'doc upserted: ' + tojson(doc));
// Cannot modify _id!
compoundColl.remove({}, false);
assert.writeError(compoundColl.update({ b: 100 }, { a: 1, b: 1, _id: 1 }, true));
doc = compoundColl.findOne();
assert(doc == null, 'doc upserted: ' + tojson(doc));
compoundColl.remove({}, false);
assert.writeError(compoundColl.update({ b: 100 }, { $set: { b: 1 }}, true, true));
doc = compoundColl.findOne();
assert(doc == null, 'doc upserted: ' + tojson(doc));
compoundColl.remove({}, false);
assert.writeError(compoundColl.update({ b: 100 }, { $set: { a: 1 }}, true, true));
doc = compoundColl.findOne();
assert(doc == null, 'doc upserted: ' + tojson(doc));
compoundColl.remove({}, false);
assert.writeError(compoundColl.update({ b: 100 }, { $set: { a: 1, b: 1 }}, true, true));
doc = compoundColl.findOne();
assert(doc == null, 'doc upserted: ' + tojson(doc));
// Cannot modify _id!
compoundColl.remove({}, false);
assert.writeError(compoundColl.update({ b: 100 }, { $set: { a: 1, b: 1, _id: 1 }}, true, true));
doc = compoundColl.findOne();
assert(doc == null, 'doc upserted: ' + tojson(doc));
compoundColl.remove({}, false);
assert.writeError(compoundColl.update({ b: 100 }, { $set: { c: 1 }}, true, true));
doc = compoundColl.findOne();
assert(doc == null, 'doc upserted: ' + tojson(doc));
//
// Full skey query update
//
compoundColl.remove({}, false);
compoundColl.insert({ a: 100, b: 100 });
assert.writeError(compoundColl.update({ a: 100, b: 100 }, { a: 100 }, false));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc));
compoundColl.remove({}, false);
compoundColl.insert({ a: 100, b: 100 });
assert.writeOK(compoundColl.update({ a: 100, b: 100 }, { a: 100, b: 100, c: 100 }, false));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100, c: 100 }), 'doc did not change: ' + tojson(doc));
// Cannot modify _id!
compoundColl.remove({}, false);
compoundColl.insert({ a: 100, b: 100 });
assert.writeError(compoundColl.update({ a: 100, b: 100 }, { a: 100, b: 100, _id: 100 }, false));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc));
compoundColl.remove({}, false);
compoundColl.insert({ a: 100, b: 100 });
assert.writeError(compoundColl.update({ a: 100, b: 100 }, { b: 100 }, false));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc));
compoundColl.remove({}, false);
compoundColl.insert({ a: 100, b: 100 });
assert.writeOK(compoundColl.update({ a: 100, b: 100 }, { $set: { b: 100, c: 1 }}, false, true));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100, c: 1 }), 'doc did not change: ' + tojson(doc));
compoundColl.remove({}, false);
compoundColl.insert({ a: 100, b: 100 });
assert.writeOK(compoundColl.update({ a: 100, b: 100 }, { $set: { a: 100, b: 100, c: 1 }}, false, true));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100, c: 1 }), 'doc did not change: ' + tojson(doc));
// Cannot modify _id!
compoundColl.remove({}, false);
compoundColl.insert({ a: 100, b: 100 });
assert.writeError(compoundColl.update({ a: 100, b: 100 }, { $set: { a: 100, b: 100, _id: 1 }}, false, true));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc));
compoundColl.remove({}, false);
compoundColl.insert({ a: 100, b: 100 });
assert.writeError(compoundColl.update({ a: 100, b: 100 }, { $set: { a: 100, b: 2, c: 1 }}, false, true));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc));
compoundColl.remove({}, false);
compoundColl.insert({ a: 100, b: 100 });
assert.writeOK(compoundColl.update({ a: 100, b: 100 }, { $set: { c: 1 }}, false, true));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100, c: 1 }), 'doc did not change: ' + tojson(doc));
//
// Full skey query upsert
//
compoundColl.remove({}, false);
assert.writeError(compoundColl.update({ a: 100, b: 100 }, { a: 100 }, true));
doc = compoundColl.findOne();
assert(doc == null, 'doc was upserted: ' + tojson(doc));
compoundColl.remove({}, false);
assert.writeOK(compoundColl.update({ a: 100, b: 100 }, { a: 100, b: 100, c: 1 }, true));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100, c: 1 }), 'wrong doc: ' + tojson(doc));
// Cannot modify _id!
compoundColl.remove({}, false);
assert.writeOK(compoundColl.update({ a: 100, b: 100 }, { a: 100, b: 100, _id: 100 }, true));
doc = compoundColl.findOne();
assert(friendlyEqual(doc, { _id: 100, a: 100, b: 100 }), 'wrong doc: ' + tojson(doc));
compoundColl.remove({}, false);
assert.writeError(compoundColl.update({ a: 100, b: 100 }, { b: 100 }, true));
doc = compoundColl.findOne();
assert(doc == null, 'doc was upserted: ' + tojson(doc));
compoundColl.remove({}, false);
assert.writeOK(compoundColl.update({ a: 100, b: 100 }, { $set: { b: 100, c: 1 }}, true, true));
doc = compoundColl.findOne();
assert(doc != null, 'doc was not upserted: ' + tojson(doc));
compoundColl.remove({}, false);
assert.writeOK(compoundColl.update({ a: 100, b: 100 }, { $set: { a: 100, b: 100, c: 1 }}, true, true));
doc = compoundColl.findOne();
assert(doc != null, 'doc was not upserted: ' + tojson(doc));
// Can upsert with new _id
compoundColl.remove({}, false);
assert.writeOK(compoundColl.update({ a: 100, b: 100 }, { $set: { a: 100, b: 100, _id: 1 }}, true, true));
doc = compoundColl.findOne();
assert(doc != null, 'doc was not upserted: ' + tojson(doc));
compoundColl.remove({}, false);
assert.writeError(compoundColl.update({ a: 100, b: 100 }, { $set: { a: 100, b: 2, c: 1 }}, true, true));
doc = compoundColl.findOne();
assert(doc == null, 'doc was upserted: ' + tojson(doc));
compoundColl.remove({}, false);
assert.writeOK(compoundColl.update({ a: 100, b: 100 }, { $set: { c: 1 }}, true, true));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100, c: 1 }), 'wrong doc: ' + tojson(doc));
//
// _id query update
//
compoundColl.remove({}, false);
compoundColl.insert({ _id: 1, a: 100, b: 100 });
assert.writeError(compoundColl.update({ _id: 1 }, { a: 1 }));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc));
// Special case for _id. This is for making save method work.
compoundColl.remove({}, false);
compoundColl.insert({ _id: 1, a: 100, b: 100 });
assert.writeOK(compoundColl.update({ _id: 1 }, { a: 100, b: 100 }));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc));
compoundColl.remove({}, false);
compoundColl.insert({ _id: 1, a: 100, b: 100 });
assert.writeError(compoundColl.update({ _id: 1 }, { a: 1, b: 1 }));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc));
compoundColl.remove({}, false);
compoundColl.insert({ _id: 1, a: 100, b: 100 });
assert.writeError(compoundColl.update({ _id: 1 }, { $set: { a: 1 }}, false, true));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc));
compoundColl.remove({}, false);
compoundColl.insert({ _id: 1, a: 100, b: 100 });
assert.writeOK(compoundColl.update({ _id: 1 }, { $set: { a: 100 }}, false, true));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc));
compoundColl.remove({}, false);
compoundColl.insert({ _id: 1, a: 100, b: 100 });
assert.writeError(compoundColl.update({ _id: 1 }, { $set: { b: 1 }}, false, true));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc));
compoundColl.remove({}, false);
compoundColl.insert({ _id: 1, a: 100, b: 100 });
assert.writeOK(compoundColl.update({ _id: 1 }, { $set: { b: 100 }}, false, true));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc));
compoundColl.remove({}, false);
compoundColl.insert({ _id: 1, a: 100, b: 100 });
assert.writeError(compoundColl.update({ _id: 1 }, { $set: { a: 1, b: 1 }}, false, true));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 100, b: 100 }), 'doc changed: ' + tojson(doc));
//
// _id query upsert
//
compoundColl.remove({}, false);
assert.writeError(compoundColl.update({ _id: 1 }, { a: 1 }, true));
doc = compoundColl.findOne();
assert(doc == null, 'doc was upserted: ' + tojson(doc));
compoundColl.remove({}, false);
assert.writeOK(compoundColl.update({ _id: 1 }, { a: 1, b: 1 }, true));
doc = compoundColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { a: 1, b: 1 }), 'bad doc: ' + tojson(doc));
compoundColl.remove({}, false);
assert.writeError(compoundColl.update({ _id: 1 }, { $set: { a: 1 }}, true, true));
doc = compoundColl.findOne();
assert(doc == null, 'doc was upserted: ' + tojson(doc));
compoundColl.remove({}, false);
assert.writeError(compoundColl.update({ _id: 1 }, { $set: { b: 1 }}, true, true));
doc = compoundColl.findOne();
assert(doc == null, 'doc was upserted: ' + tojson(doc));
compoundColl.remove({}, false);
assert.writeError(compoundColl.update({ _id: 1 }, { $set: { a: 1, b: 1 }}, true, true));
assert.eq(0, compoundColl.count(), 'doc should not be inserted');
//
// Dotted query update
//
dotColl.remove({}, false);
dotColl.insert({ x: { a: 100 }});
assert.writeOK(dotColl.update({ 'x.a': 100 }, { x: { a: 100, b: 2 }}));
doc = dotColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { x: { a: 100, b: 2 }}), 'doc did not change: ' + tojson(doc));
// Dotted field names in the resulting objects should not be allowed.
// This check currently resides in the client drivers.
dotColl.remove({}, false);
dotColl.insert({ x: { a: 100 }});
assert.throws(function() {
dotColl.update({ 'x.a': 100 }, { x: { 'a.z': 100 }});
});
// Dotted field names in the resulting objects should not be allowed.
// This check currently resides in the client drivers.
dotColl.remove({}, false);
dotColl.insert({ x: { a: 100 }});
assert.throws(function() {
dotColl.update({ 'x.a': 100 }, { 'x.a': 100 });
});
// Dotted field names in the resulting objects should not be allowed.
// This check currently resides in the client drivers.
dotColl.remove({}, false);
dotColl.insert({ x: { a: 100 }});
assert.throws(function() {
dotColl.update({ 'x.a': 100 }, { 'x.a.z': 100 });
});
dotColl.remove({}, false);
dotColl.insert({ x: { a: 100 }});
assert.writeError(dotColl.update({ 'x.a': 100 }, { x: 100 }));
doc = dotColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { x: { a: 100 }}), 'doc changed: ' + tojson(doc));
dotColl.remove({}, false);
dotColl.insert({ x: { a: 100 }});
assert.writeError(dotColl.update({ 'x.a': 100 }, { x: { b: 100 }}));
doc = dotColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { x: { a: 100 }}), 'doc changed: ' + tojson(doc));
dotColl.remove({}, false);
dotColl.insert({ x: { a: 100 }});
assert.writeOK(dotColl.update({ 'x.a': 100 }, { $set: { x: { a: 100, b: 2 }}}, false, true));
doc = dotColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { x: { a: 100, b: 2 }}), 'doc did not change: ' + tojson(doc));
dotColl.remove({}, false);
dotColl.insert({ x: { a: 100 }});
assert.writeError(dotColl.update({ 'x.a': 100 }, { $set: { x: { a: 2 }}}, false, true));
doc = dotColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { x: { a: 100 }}), 'doc changed: ' + tojson(doc));
dotColl.remove({}, false);
dotColl.insert({ x: { a: 100 }});
assert.writeError(dotColl.update({ 'x.a': 100 }, { $set: { x: { b: 100 }}}, false, true));
doc = dotColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { x: { a: 100 }}), 'doc changed: ' + tojson(doc));
dotColl.remove({}, false);
dotColl.insert({ x: { a: 100 }});
assert.writeOK(dotColl.update({ 'x.a': 100 }, { $set: { 'x.a': 100, b: 2 }}, false, true));
doc = dotColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { x: { a: 100 }, b: 2 }), 'doc did not change: ' + tojson(doc));
dotColl.remove({}, false);
dotColl.insert({ x: { a: 100 }});
assert.writeError(dotColl.update({ 'x.a': 100 }, { $set: { x: { 'a.z': 100 }}}, false, true));
doc = dotColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { x: { a: 100 }}), 'doc changed: ' + tojson(doc));
dotColl.remove({}, false);
dotColl.insert({ x: { a: 100 }});
assert.writeError(dotColl.update({ 'x.a': 100 }, { $set: { 'x.a.z': 100 }}, false, true));
doc = dotColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { x: { a: 100 }}), 'doc changed: ' + tojson(doc));
dotColl.remove({}, false);
dotColl.insert({ x: { a: 100 }});
assert.writeError(dotColl.update({ 'x.a': 100 }, { $set: { x: 100 }}, false, true));
doc = dotColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { x: { a: 100 }}), 'doc changed: ' + tojson(doc));
dotColl.remove({}, false);
dotColl.insert({ x: { a: 100 }});
assert.writeOK(dotColl.update({ 'x.a': 100 }, { $set: { 'x.b': 200 }}, false, true));
doc = dotColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { x: { a: 100, b: 200 }}), 'doc did not change: ' + tojson(doc));
//
// Dotted query upsert
//
dotColl.remove({}, false);
assert.writeOK(dotColl.update({ 'x.a': 100 }, { x: { a: 100, b: 2 }}, true));
doc = dotColl.findOne();
assert(doc != null, 'doc was not upserted: ' + tojson(doc));
// Dotted field names in the resulting objects should not be allowed.
// This check currently resides in the client drivers.
dotColl.remove({}, false);
assert.throws(function() {
dotColl.update({ 'x.a': 100 }, { x: { 'a.z': 100 }}, true);
});
// Dotted field names in the resulting objects should not be allowed.
// This check currently resides in the client drivers.
dotColl.remove({}, false);
assert.throws(function() {
dotColl.update({ 'x.a': 100 }, { 'x.a': 100 }, true);
});
// Dotted field names in the resulting objects should not be allowed.
// This check currently resides in the client drivers.
dotColl.remove({}, false);
assert.throws(function() {
dotColl.update({ 'x.a': 100 }, { 'x.a.z': 100 }, true);
});
dotColl.remove({}, false);
assert.writeError(dotColl.update({ 'x.a': 100 }, { x: 100 }, true));
doc = dotColl.findOne();
assert(doc == null, 'doc was upserted: ' + tojson(doc));
dotColl.remove({}, false);
assert.writeError(dotColl.update({ 'x.a': 100 }, { x: { b: 100 }}, true));
doc = dotColl.findOne();
assert(doc == null, 'doc was upserted: ' + tojson(doc));
dotColl.remove({}, false);
assert.writeOK(dotColl.update({ 'x.a': 100 }, { $set: { x: { a: 100, b: 2 }}}, true));
doc = dotColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { x: { a: 100, b: 2 }}), 'bad doc: ' + tojson(doc));
dotColl.remove({}, false);
assert.writeError(dotColl.update({ 'x.a': 100 }, { $set: { x: { a: 2 }}}, true));
doc = dotColl.findOne();
assert(doc == null, 'doc was upserted: ' + tojson(doc));
dotColl.remove({}, false);
assert.writeError(dotColl.update({ 'x.a': 100 }, { $set: { x: { b: 100 }}}, true));
doc = dotColl.findOne();
assert(doc == null, 'doc was upserted: ' + tojson(doc));
dotColl.remove({}, false);
assert.writeOK(dotColl.update({ 'x.a': 100 }, { $set: { 'x.a': 100, b: 3 }}, true));
doc = dotColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { x: { a: 100 }, b: 3 }), 'bad doc: ' + tojson(doc));
dotColl.remove({}, false);
assert.writeError(dotColl.update({ 'x.a': 100 }, { $set: { 'x.a': 2 }}, true));
doc = dotColl.findOne();
assert(doc == null, 'doc was upserted: ' + tojson(doc));
dotColl.remove({}, false);
assert.writeError(dotColl.update({ 'x.a': 100 }, { $set: { x: { 'a.z': 100 }}}, true));
doc = dotColl.findOne();
assert(doc == null, 'doc was upserted: ' + tojson(doc));
dotColl.remove({}, false);
assert.writeError(dotColl.update({ 'x.a': 100 }, { $set: { 'x.a.z': 100 }}, true));
doc = dotColl.findOne();
assert(doc == null, 'doc was upserted: ' + tojson(doc));
dotColl.remove({}, false);
assert.writeError(dotColl.update({ 'x.a': 100 }, { $set: { x: 100 }}, true));
doc = dotColl.findOne();
assert(doc == null, 'doc was upserted: ' + tojson(doc));
dotColl.remove({}, false);
assert.writeOK(dotColl.update({ 'x.a': 100 }, { $set: { 'x.b': 2 }}, true));
doc = dotColl.findOne();
delete doc._id;
assert(friendlyEqual(doc, { x: { a: 100, b: 2 }}), 'bad doc: ' + tojson(doc));
st.stop();