diff options
author | David Storch <david.storch@10gen.com> | 2014-04-14 13:31:31 -0400 |
---|---|---|
committer | Matt Kangas <matt.kangas@mongodb.com> | 2014-04-15 18:44:27 -0400 |
commit | 1b7b02db89e8a574cb2f5bff717090a8719d4fb9 (patch) | |
tree | 6e34bfd1917827637c2e714f29196cbb57d82447 | |
parent | a386aa60368efb333424ab4bf7a9385d6c45a03a (diff) | |
download | mongo-1b7b02db89e8a574cb2f5bff717090a8719d4fb9.tar.gz |
SERVER-13562 limit and skip stages no longer return eof without calling work() on the child
Doing so could cause missing results for a tailable cursor.
(cherry picked from commit 9d1f365bf8f0c3d6192284cffab4e3927722e17c)
-rw-r--r-- | jstests/core/tailable_skip_limit.js | 63 | ||||
-rw-r--r-- | src/mongo/db/exec/limit.cpp | 6 | ||||
-rw-r--r-- | src/mongo/db/exec/skip.cpp | 2 |
3 files changed, 67 insertions, 4 deletions
diff --git a/jstests/core/tailable_skip_limit.js b/jstests/core/tailable_skip_limit.js new file mode 100644 index 00000000000..d0da0a15b28 --- /dev/null +++ b/jstests/core/tailable_skip_limit.js @@ -0,0 +1,63 @@ +// Test that tailable cursors work correctly with skip and limit. + +// Setup the capped collection. +var collname = "jstests_tailable_skip_limit" +var t = db[collname]; +t.drop(); +db.createCollection(collname, {capped: true, size: 1024}); + +t.save({_id: 1}); +t.save({_id: 2}); + +// Non-tailable with skip +var cursor = t.find().skip(1); +assert.eq(2, cursor.next()["_id"]); +assert(!cursor.hasNext()); +t.save({_id: 3}); +assert(!cursor.hasNext()); + +// Non-tailable with limit +var cursor = t.find().limit(100); +for (var i = 1; i <= 3; i++) { + assert.eq(i, cursor.next()["_id"]); +} +assert(!cursor.hasNext()); +t.save({_id: 4}); +assert(!cursor.hasNext()); + +// Non-tailable with negative limit +var cursor = t.find().limit(-100); +for (var i = 1; i <= 4; i++) { + assert.eq(i, cursor.next()["_id"]); +} +assert(!cursor.hasNext()); +t.save({_id: 5}); +assert(!cursor.hasNext()); + +// Tailable with skip +cursor = t.find().addOption(2).skip(4); +assert.eq(5, cursor.next()["_id"]); +assert(!cursor.hasNext()); +t.save({_id: 6}); +assert(cursor.hasNext()); +assert.eq(6, cursor.next()["_id"]); + +// Tailable with limit +var cursor = t.find().addOption(2).limit(100); +for (var i = 1; i <= 6; i++) { + assert.eq(i, cursor.next()["_id"]); +} +assert(!cursor.hasNext()); +t.save({_id: 7}); +assert(cursor.hasNext()); +assert.eq(7, cursor.next()["_id"]); + +// Tailable with negative limit +var cursor = t.find().addOption(2).limit(-100); +for (var i = 1; i <= 7; i++) { + assert.eq(i, cursor.next()["_id"]); +} +assert(!cursor.hasNext()); +t.save({_id: 8}); +assert(cursor.hasNext()); +assert.eq(8, cursor.next()["_id"]); diff --git a/src/mongo/db/exec/limit.cpp b/src/mongo/db/exec/limit.cpp index da0b9e03cc1..e084781dbaf 100644 --- a/src/mongo/db/exec/limit.cpp +++ b/src/mongo/db/exec/limit.cpp @@ -42,8 +42,10 @@ namespace mongo { PlanStage::StageState LimitStage::work(WorkingSetID* out) { ++_commonStats.works; - // If we've returned as many results as we're limited to, isEOF will be true. - if (isEOF()) { return PlanStage::IS_EOF; } + if (0 == _numToReturn) { + // We've returned as many results as we're limited to. + return PlanStage::IS_EOF; + } WorkingSetID id = WorkingSet::INVALID_ID; StageState status = _child->work(&id); diff --git a/src/mongo/db/exec/skip.cpp b/src/mongo/db/exec/skip.cpp index b79bdccb307..04eaf4ae140 100644 --- a/src/mongo/db/exec/skip.cpp +++ b/src/mongo/db/exec/skip.cpp @@ -42,8 +42,6 @@ namespace mongo { PlanStage::StageState SkipStage::work(WorkingSetID* out) { ++_commonStats.works; - if (isEOF()) { return PlanStage::IS_EOF; } - WorkingSetID id = WorkingSet::INVALID_ID; StageState status = _child->work(&id); |