summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Storch <david.storch@10gen.com>2014-04-14 13:31:31 -0400
committerMatt Kangas <matt.kangas@mongodb.com>2014-04-15 18:44:27 -0400
commit1b7b02db89e8a574cb2f5bff717090a8719d4fb9 (patch)
tree6e34bfd1917827637c2e714f29196cbb57d82447
parenta386aa60368efb333424ab4bf7a9385d6c45a03a (diff)
downloadmongo-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.js63
-rw-r--r--src/mongo/db/exec/limit.cpp6
-rw-r--r--src/mongo/db/exec/skip.cpp2
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);