From d34931161eb5ec47029f8c69d731ed9360f51b9a Mon Sep 17 00:00:00 2001 From: David Percy Date: Tue, 15 Nov 2022 16:40:41 +0000 Subject: SERVER-71387 Add missing typecheck for range-based window --- .../sources/setWindowFields/range_wrong_type.js | 36 ++++++++++++++++++++++ .../window_function/partition_iterator.cpp | 9 ++++-- 2 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 jstests/aggregation/sources/setWindowFields/range_wrong_type.js diff --git a/jstests/aggregation/sources/setWindowFields/range_wrong_type.js b/jstests/aggregation/sources/setWindowFields/range_wrong_type.js new file mode 100644 index 00000000000..26001c40888 --- /dev/null +++ b/jstests/aggregation/sources/setWindowFields/range_wrong_type.js @@ -0,0 +1,36 @@ +/** + * Test that a window of the form [+N, unbounded] does not trigger a tassert + * on mixed-type input. + * + * Originally intended to reproduce SERVER-71387. + */ +(function() { +"use strict"; + +const coll = db.set_window_fields_range_wrong_type; +coll.drop(); +assert.commandWorked(coll.insert([ + // Numbers sort before strings, so we'll scan {a: 2} first. + {a: 2}, + {a: 'xyz'}, +])); + +const err = assert.throws(() => { + return coll + .aggregate({ + $setWindowFields: { + sortBy: {a: 1}, + output: { + // The lower bound +3 excludes the current document {a: 2}. + // The only remaining document is {a: 'xyz'}. + // We wrongly consider {a: 'xyz'} to be 'within' the lower bound. + // Then, when we search for the upper bound, we are surprised + // to be starting from 'xyz' which is the wrong type. + b: {$max: 5, window: {range: [+3, 'unbounded']}}, + }, + } + }) + .toArray(); +}); +assert.eq(err.code, 5429414, err); +})(); diff --git a/src/mongo/db/pipeline/window_function/partition_iterator.cpp b/src/mongo/db/pipeline/window_function/partition_iterator.cpp index 95a0935d86c..7ec49c6132f 100644 --- a/src/mongo/db/pipeline/window_function/partition_iterator.cpp +++ b/src/mongo/db/pipeline/window_function/partition_iterator.cpp @@ -302,8 +302,13 @@ optional> PartitionIterator::getEndpointsRangeBased( for (int i = start; (doc = (*this)[i]); ++i) { Value v = (*_sortExpr)->evaluate(*doc, &_expCtx->variables); if (!lessThan(v, threshold)) { - // This is the first doc we've scanned that crossed the threshold. - return i; + // This is the first doc we've scanned that crossed the threshold, + // so it's the first doc in the window (as long as it's the expected type). + if (hasExpectedType(v)) { + return i; + } else { + return boost::none; + } } } // We scanned every document in the partition, and none crossed the -- cgit v1.2.1