mirror of https://github.com/mongodb/mongo
175 lines
7.8 KiB
JavaScript
175 lines
7.8 KiB
JavaScript
// @tags: [do_not_wrap_aggregations_in_facets,]
|
|
(function() {
|
|
"use strict";
|
|
load("jstests/libs/sbe_assert_error_override.js"); // Override error-code-checking APIs.
|
|
// TODO BACKPORT-19405, BACKPORT-19406: SBE and classic have different behaviors documented in this
|
|
// test. These are unified by the SERVER tickets of the mentioned backports.
|
|
load("jstests/libs/sbe_util.js"); // For checkSBEEnabled.
|
|
|
|
const coll = db.getSiblingDB(jsTestName()).coll;
|
|
coll.drop();
|
|
|
|
const isSbeEnabled = checkSBEEnabled(db, ["featureFlagSbeFull"]);
|
|
|
|
function getResultOfExpression(expr) {
|
|
const resultArray = coll.aggregate({$project: {computed: expr}}).toArray();
|
|
assert.eq(1, resultArray.length, "ERROR from " + tojson(expr));
|
|
return resultArray[0].computed;
|
|
}
|
|
|
|
assert.commandWorked(coll.insert({
|
|
_id: 0,
|
|
decimalVal: NumberDecimal("819.5359123621083"),
|
|
doubleVal: 819.536,
|
|
int64Val: NumberLong(820),
|
|
int32Val: NumberInt(820),
|
|
dateVal: ISODate("2019-01-30T07:30:10.137Z"),
|
|
overflowDecimal: NumberDecimal("1e5000"),
|
|
overflowDouble: 1e1000,
|
|
overflowInt64: NumberLong("9223372036854775807"),
|
|
nanDouble: NaN,
|
|
nanDecimal: NumberDecimal("NaN"),
|
|
}));
|
|
|
|
// Adding a Decimal128 value to a date literal.
|
|
assert.eq(ISODate("2019-01-30T07:30:10.957Z"),
|
|
getResultOfExpression({$add: ["$decimalVal", ISODate("2019-01-30T07:30:10.137Z")]}));
|
|
assert.eq(ISODate("2019-01-30T07:30:10.957Z"),
|
|
getResultOfExpression({$add: [ISODate("2019-01-30T07:30:10.137Z"), "$decimalVal"]}));
|
|
|
|
// Adding a Decimal128 literal to a date value.
|
|
assert.eq(ISODate("2019-01-30T07:30:10.957Z"),
|
|
getResultOfExpression({$add: ["$dateVal", NumberDecimal("819.5359123621083")]}));
|
|
assert.eq(ISODate("2019-01-30T07:30:10.957Z"),
|
|
getResultOfExpression({$add: [NumberDecimal("819.5359123621083"), "$dateVal"]}));
|
|
|
|
// Adding a Decimal128 value to a date value.
|
|
assert.eq(ISODate("2019-01-30T07:30:10.957Z"),
|
|
getResultOfExpression({$add: ["$dateVal", "$decimalVal"]}));
|
|
assert.eq(ISODate("2019-01-30T07:30:10.957Z"),
|
|
getResultOfExpression({$add: ["$decimalVal", "$dateVal"]}));
|
|
|
|
// Adding a double value to a date value.
|
|
assert.eq(ISODate("2019-01-30T07:30:10.957Z"),
|
|
getResultOfExpression({$add: ["$dateVal", "$doubleVal"]}));
|
|
assert.eq(ISODate("2019-01-30T07:30:10.957Z"),
|
|
getResultOfExpression({$add: ["$doubleVal", "$dateVal"]}));
|
|
|
|
// Adding an int64_t value to date value.
|
|
assert.eq(ISODate("2019-01-30T07:30:10.957Z"),
|
|
getResultOfExpression({$add: ["$dateVal", "$int64Val"]}));
|
|
assert.eq(ISODate("2019-01-30T07:30:10.957Z"),
|
|
getResultOfExpression({$add: ["$int64Val", "$dateVal"]}));
|
|
|
|
// Adding an int32_t value to date value.
|
|
assert.eq(ISODate("2019-01-30T07:30:10.957Z"),
|
|
getResultOfExpression({$add: ["$dateVal", "$int32Val"]}));
|
|
assert.eq(ISODate("2019-01-30T07:30:10.957Z"),
|
|
getResultOfExpression({$add: ["$int32Val", "$dateVal"]}));
|
|
|
|
// Addition with a date and multiple values of differing data types.
|
|
// SBE uses doubleDoubleSum for $add whereas classic does not.
|
|
if (isSbeEnabled) {
|
|
assert.eq(
|
|
ISODate("2019-01-30T07:30:12.596Z"),
|
|
getResultOfExpression({$add: ["$dateVal", "$decimalVal", "$doubleVal", "$int64Val"]}));
|
|
assert.eq(
|
|
ISODate("2019-01-30T07:30:12.596Z"),
|
|
getResultOfExpression({$add: ["$decimalVal", "$dateVal", "$doubleVal", "$int64Val"]}));
|
|
} else {
|
|
assert.eq(
|
|
ISODate("2019-01-30T07:30:12.597Z"),
|
|
getResultOfExpression({$add: ["$dateVal", "$decimalVal", "$doubleVal", "$int64Val"]}));
|
|
assert.eq(
|
|
ISODate("2019-01-30T07:30:12.597Z"),
|
|
getResultOfExpression({$add: ["$decimalVal", "$dateVal", "$doubleVal", "$int64Val"]}));
|
|
}
|
|
assert.eq(ISODate("2019-01-30T07:30:12.596Z"),
|
|
getResultOfExpression({$add: ["$decimalVal", "$doubleVal", "$int64Val", "$dateVal"]}));
|
|
// The result of an addition must remain in the range of int64_t in order to convert back to a Date;
|
|
// an overflow into the domain of double-precision floating point numbers triggers a query-fatal
|
|
// error.
|
|
assert.throwsWithCode(() => getResultOfExpression({$add: ["$dateVal", "$overflowDouble"]}),
|
|
ErrorCodes.Overflow);
|
|
|
|
assert.throwsWithCode(() => getResultOfExpression({$add: ["$dateVal", "$overflowInt64"]}),
|
|
ErrorCodes.Overflow);
|
|
|
|
assert.throwsWithCode(
|
|
() => getResultOfExpression({$add: ["$dateVal", "$int64Val", "$overflowDouble"]}),
|
|
ErrorCodes.Overflow);
|
|
|
|
assert.throwsWithCode(
|
|
() => getResultOfExpression({$add: ["$int64Val", "$dateVal", "$overflowDouble"]}),
|
|
ErrorCodes.Overflow);
|
|
|
|
// An overflow into the domain of Decimal128 results in an overflow exception.
|
|
// SBE overflows to return nan, whereas classic throws.
|
|
if (isSbeEnabled) {
|
|
// One quirk of date addition semantics is that an overflow into the domain of Decimal128 is not
|
|
// fatal and instead results in an invalid "NaN" Date value.
|
|
const nanDate = new Date("");
|
|
assert.eq(nanDate, getResultOfExpression({$add: ["$dateVal", "$overflowDecimal"]}));
|
|
assert.eq(nanDate,
|
|
getResultOfExpression({$add: ["$int64Val", "$dateVal", "$overflowDecimal"]}));
|
|
assert.eq(nanDate,
|
|
getResultOfExpression({$add: ["$dateVal", "$overflowDouble", "$overflowDecimal"]}));
|
|
} else {
|
|
assert.throwsWithCode(() => getResultOfExpression({$add: ["$dateVal", "$overflowDecimal"]}),
|
|
ErrorCodes.Overflow);
|
|
assert.throwsWithCode(
|
|
() => getResultOfExpression({$add: ["$int64Val", "$dateVal", "$overflowDecimal"]}),
|
|
ErrorCodes.Overflow);
|
|
assert.throwsWithCode(
|
|
() => getResultOfExpression({$add: ["$dateVal", "$overflowDouble", "$overflowDecimal"]}),
|
|
ErrorCodes.Overflow);
|
|
}
|
|
|
|
// Adding a double-typed NaN to a date value.
|
|
assert.throwsWithCode(() => getResultOfExpression({$add: ["$dateVal", "$nanDouble"]}),
|
|
ErrorCodes.Overflow);
|
|
|
|
assert.throwsWithCode(() => getResultOfExpression({$add: ["$nanDouble", "$dateVal"]}),
|
|
ErrorCodes.Overflow);
|
|
|
|
// An NaN Decimal128 added to date results in an overflow exception.
|
|
// SBE overflows to return NaN, whereas classic throws.
|
|
if (isSbeEnabled) {
|
|
const nanDate = new Date("");
|
|
assert.eq(nanDate, getResultOfExpression({$add: ["$dateVal", "$nanDecimal"]}));
|
|
assert.eq(nanDate, getResultOfExpression({$add: ["$nanDecimal", "$dateVal"]}));
|
|
} else {
|
|
assert.throwsWithCode(() => getResultOfExpression({$add: ["$dateVal", "$nanDecimal"]}),
|
|
ErrorCodes.Overflow);
|
|
assert.throwsWithCode(() => getResultOfExpression({$add: ["$nanDecimal", "$dateVal"]}),
|
|
ErrorCodes.Overflow);
|
|
}
|
|
// Addition with a date, a double-typed NaN, and a third value.
|
|
assert.throwsWithCode(() => getResultOfExpression({$add: ["$dateVal", "$doubleVal", "$nanDouble"]}),
|
|
ErrorCodes.Overflow);
|
|
|
|
// Addition with a date, and both types of NaN.
|
|
// SBE returns NaN if it's a value in $add, whereas classic throws.
|
|
if (isSbeEnabled) {
|
|
const nanDate = new Date("");
|
|
assert.eq(nanDate, getResultOfExpression({$add: ["$dateVal", "$nanDouble", "$nanDecimal"]}));
|
|
} else {
|
|
assert.throwsWithCode(
|
|
() => getResultOfExpression({$add: ["$dateVal", "$nanDouble", "$nanDecimal"]}),
|
|
ErrorCodes.Overflow);
|
|
}
|
|
// Throw error when there're two or more date in $add.
|
|
assert.throwsWithCode(() => getResultOfExpression({$add: ["$dateVal", 1, "$dateVal"]}), 4974202);
|
|
|
|
// Test very large long and verify that we're maintaining the precision of long arithmetic.
|
|
// 2397083434877565865 and 239708343487756586 both cast to the same double value from longs
|
|
assert.eq(ISODate("2019-01-30T07:30:10.958Z"), getResultOfExpression({
|
|
$add: [
|
|
"$dateVal",
|
|
NumberLong("2397083434877565865"),
|
|
"$doubleVal",
|
|
NumberLong("-2397083434877565864")
|
|
]
|
|
}));
|
|
}());
|