0
0
mirror of https://github.com/mongodb/mongo.git synced 2024-12-01 09:32:32 +01:00

SERVER-60588 Don't attempt to coerce a double product to long

This commit is contained in:
Jennifer Peshansky 2021-10-13 20:26:26 +00:00 committed by Evergreen Agent
parent 28c55c9701
commit 3df7340a53
2 changed files with 27 additions and 16 deletions

View File

@ -15,8 +15,7 @@ coll.drop();
out.drop();
const nDocs = 50;
// We seed the documents with large values for a. This enables the pipeline that exposes the
// halloween problem to overflow and fail more quickly.
// We seed the documents with large values for a.
const largeNum = 1000 * 1000 * 1000;
// We set internalQueryExecYieldPeriodMS to 1 ms to have query execution yield as often as
@ -32,7 +31,7 @@ assert.commandWorked(db.adminCommand({setParameter: 1, internalQueryExecYieldPer
// multiple times.
function insertDocuments(collObject) {
const bulk = collObject.initializeUnorderedBulkOp();
for (let i = 0; i < nDocs; i++) {
for (let i = 1; i < nDocs; i++) {
bulk.insert({_id: i, a: i * largeNum, largeArray: (new Array(1024 * 1024).join("a"))});
}
assert.commandWorked(bulk.execute());
@ -57,25 +56,33 @@ function pipeline(outColl) {
const differentCollPipeline = pipeline(out.getName());
const sameCollPipeline = pipeline(coll.getName());
// Targeting a collection that is not the collection being agggregated over will result in each
// Targeting a collection that is not the collection being aggregated over will result in each
// document's value of 'a' being updated exactly once.
assert.commandWorked(
db.runCommand({aggregate: coll.getName(), pipeline: differentCollPipeline, cursor: {}}));
// Filter out 'largeArray' as we are only interested in verifying the value of "a" in each
// document.
const result = out.find({}, {largeArray: 0}).toArray();
const diffCollResult = out.find({}, {largeArray: 0}).toArray();
for (const doc of result) {
for (const doc of diffCollResult) {
assert(doc.hasOwnProperty("a"), doc);
const expectedVal = doc["_id"] * 2 * largeNum;
assert.eq(doc["a"], expectedVal, doc);
}
// Targeting the same collection that is being aggregated over will still result in each
// document's value of 'a' being updated exactly once.
assert.commandWorked(
db.runCommand({aggregate: coll.getName(), pipeline: sameCollPipeline, cursor: {}}));
const sameCollResult = out.find({}, {largeArray: 0}).toArray();
for (const doc of sameCollResult) {
assert(doc.hasOwnProperty("a"), doc);
const expectedVal = doc["_id"] * 2 * largeNum;
assert.eq(doc["a"], expectedVal, doc);
}
// Because this pipeline writes to the collection being aggregated, it will cause documents to be
// updated and pushed forward indefinitely. This will cause the computed values to eventually
// overflow.
assert.commandFailedWithCode(
db.runCommand({aggregate: coll.getName(), pipeline: sameCollPipeline, cursor: {}}), 31109);
MongoRunner.stopMongod(conn);
}());

View File

@ -3120,11 +3120,15 @@ public:
} else {
doubleProduct *= val.coerceToDouble();
if (!std::isfinite(val.coerceToDouble()) ||
overflow::mul(longProduct, val.coerceToLong(), &longProduct)) {
// The number is either Infinity or NaN, or the 'longProduct' would have
// overflowed, so we're abandoning it.
productType = NumberDouble;
if (productType != NumberDouble) {
// If `productType` is not a double, it must be one of the integer types, so we
// attempt to update `longProduct`.
if (!std::isfinite(val.coerceToDouble()) ||
overflow::mul(longProduct, val.coerceToLong(), &longProduct)) {
// The multiplier is either Infinity or NaN, or the `longProduct` would
// have overflowed, so we're abandoning it.
productType = NumberDouble;
}
}
}
}