summaryrefslogtreecommitdiff
path: root/src/mongo/dbtests/query_stage_merge_sort.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/dbtests/query_stage_merge_sort.cpp')
-rw-r--r--src/mongo/dbtests/query_stage_merge_sort.cpp1085
1 files changed, 556 insertions, 529 deletions
diff --git a/src/mongo/dbtests/query_stage_merge_sort.cpp b/src/mongo/dbtests/query_stage_merge_sort.cpp
index b05799b8588..884ec1bda55 100644
--- a/src/mongo/dbtests/query_stage_merge_sort.cpp
+++ b/src/mongo/dbtests/query_stage_merge_sort.cpp
@@ -46,541 +46,567 @@
namespace QueryStageMergeSortTests {
- using std::unique_ptr;
- using std::set;
- using std::string;
-
- class QueryStageMergeSortTestBase {
- public:
- QueryStageMergeSortTestBase() : _client(&_txn) {
-
+using std::unique_ptr;
+using std::set;
+using std::string;
+
+class QueryStageMergeSortTestBase {
+public:
+ QueryStageMergeSortTestBase() : _client(&_txn) {}
+
+ virtual ~QueryStageMergeSortTestBase() {
+ OldClientWriteContext ctx(&_txn, ns());
+ _client.dropCollection(ns());
+ }
+
+ void addIndex(const BSONObj& obj) {
+ ASSERT_OK(dbtests::createIndex(&_txn, ns(), obj));
+ }
+
+ IndexDescriptor* getIndex(const BSONObj& obj, Collection* coll) {
+ return coll->getIndexCatalog()->findIndexByKeyPattern(&_txn, obj);
+ }
+
+ void insert(const BSONObj& obj) {
+ _client.insert(ns(), obj);
+ }
+
+ void remove(const BSONObj& obj) {
+ _client.remove(ns(), obj);
+ }
+
+ void getLocs(set<RecordId>* out, Collection* coll) {
+ auto cursor = coll->getCursor(&_txn);
+ while (auto record = cursor->next()) {
+ out->insert(record->id);
}
-
- virtual ~QueryStageMergeSortTestBase() {
- OldClientWriteContext ctx(&_txn, ns());
- _client.dropCollection(ns());
+ }
+
+ BSONObj objWithMinKey(int start) {
+ BSONObjBuilder startKeyBob;
+ startKeyBob.append("", start);
+ startKeyBob.appendMinKey("");
+ return startKeyBob.obj();
+ }
+
+ BSONObj objWithMaxKey(int start) {
+ BSONObjBuilder endKeyBob;
+ endKeyBob.append("", start);
+ endKeyBob.appendMaxKey("");
+ return endKeyBob.obj();
+ }
+
+ static const char* ns() {
+ return "unittests.QueryStageMergeSort";
+ }
+
+protected:
+ OperationContextImpl _txn;
+
+private:
+ DBDirectClient _client;
+};
+
+// SERVER-1205:
+// find($or[{a:1}, {b:1}]).sort({c:1}) with indices {a:1, c:1} and {b:1, c:1}.
+class QueryStageMergeSortPrefixIndex : public QueryStageMergeSortTestBase {
+public:
+ void run() {
+ OldClientWriteContext ctx(&_txn, ns());
+ Database* db = ctx.db();
+ Collection* coll = db->getCollection(ns());
+ if (!coll) {
+ WriteUnitOfWork wuow(&_txn);
+ coll = db->createCollection(&_txn, ns());
+ wuow.commit();
}
- void addIndex(const BSONObj& obj) {
- ASSERT_OK(dbtests::createIndex(&_txn, ns(), obj));
- }
+ const int N = 50;
- IndexDescriptor* getIndex(const BSONObj& obj, Collection* coll) {
- return coll->getIndexCatalog()->findIndexByKeyPattern( &_txn, obj );
+ for (int i = 0; i < N; ++i) {
+ insert(BSON("a" << 1 << "c" << i));
+ insert(BSON("b" << 1 << "c" << i));
}
- void insert(const BSONObj& obj) {
- _client.insert(ns(), obj);
+ BSONObj firstIndex = BSON("a" << 1 << "c" << 1);
+ BSONObj secondIndex = BSON("b" << 1 << "c" << 1);
+
+ addIndex(firstIndex);
+ addIndex(secondIndex);
+
+ WorkingSet* ws = new WorkingSet();
+ // Sort by c:1
+ MergeSortStageParams msparams;
+ msparams.pattern = BSON("c" << 1);
+ MergeSortStage* ms = new MergeSortStage(msparams, ws, coll);
+
+ // a:1
+ IndexScanParams params;
+ params.descriptor = getIndex(firstIndex, coll);
+ params.bounds.isSimpleRange = true;
+ params.bounds.startKey = objWithMinKey(1);
+ params.bounds.endKey = objWithMaxKey(1);
+ params.bounds.endKeyInclusive = true;
+ params.direction = 1;
+ ms->addChild(new IndexScan(&_txn, params, ws, NULL));
+
+ // b:1
+ params.descriptor = getIndex(secondIndex, coll);
+ ms->addChild(new IndexScan(&_txn, params, ws, NULL));
+
+ // Must fetch if we want to easily pull out an obj.
+ PlanExecutor* rawExec;
+ Status status = PlanExecutor::make(&_txn,
+ ws,
+ new FetchStage(&_txn, ws, ms, NULL, coll),
+ coll,
+ PlanExecutor::YIELD_MANUAL,
+ &rawExec);
+ ASSERT_OK(status);
+ std::unique_ptr<PlanExecutor> exec(rawExec);
+
+ for (int i = 0; i < N; ++i) {
+ BSONObj first, second;
+ ASSERT_EQUALS(PlanExecutor::ADVANCED, exec->getNext(&first, NULL));
+ ASSERT_EQUALS(PlanExecutor::ADVANCED, exec->getNext(&second, NULL));
+ ASSERT_EQUALS(first["c"].numberInt(), second["c"].numberInt());
+ ASSERT_EQUALS(i, first["c"].numberInt());
+ ASSERT((first.hasField("a") && second.hasField("b")) ||
+ (first.hasField("b") && second.hasField("a")));
}
- void remove(const BSONObj& obj) {
- _client.remove(ns(), obj);
+ // Should be done now.
+ BSONObj foo;
+ ASSERT_NOT_EQUALS(PlanExecutor::ADVANCED, exec->getNext(&foo, NULL));
+ }
+};
+
+// Each inserted document appears in both indices but is deduped and returned properly/sorted.
+class QueryStageMergeSortDups : public QueryStageMergeSortTestBase {
+public:
+ void run() {
+ OldClientWriteContext ctx(&_txn, ns());
+ Database* db = ctx.db();
+ Collection* coll = db->getCollection(ns());
+ if (!coll) {
+ WriteUnitOfWork wuow(&_txn);
+ coll = db->createCollection(&_txn, ns());
+ wuow.commit();
}
- void getLocs(set<RecordId>* out, Collection* coll) {
- auto cursor = coll->getCursor(&_txn);
- while (auto record = cursor->next()) {
- out->insert(record->id);
- }
- }
+ const int N = 50;
- BSONObj objWithMinKey(int start) {
- BSONObjBuilder startKeyBob;
- startKeyBob.append("", start);
- startKeyBob.appendMinKey("");
- return startKeyBob.obj();
+ for (int i = 0; i < N; ++i) {
+ insert(BSON("a" << 1 << "b" << 1 << "c" << i));
+ insert(BSON("a" << 1 << "b" << 1 << "c" << i));
}
- BSONObj objWithMaxKey(int start) {
- BSONObjBuilder endKeyBob;
- endKeyBob.append("", start);
- endKeyBob.appendMaxKey("");
- return endKeyBob.obj();
+ BSONObj firstIndex = BSON("a" << 1 << "c" << 1);
+ BSONObj secondIndex = BSON("b" << 1 << "c" << 1);
+
+ addIndex(firstIndex);
+ addIndex(secondIndex);
+
+ WorkingSet* ws = new WorkingSet();
+ // Sort by c:1
+ MergeSortStageParams msparams;
+ msparams.pattern = BSON("c" << 1);
+ MergeSortStage* ms = new MergeSortStage(msparams, ws, coll);
+
+ // a:1
+ IndexScanParams params;
+ params.descriptor = getIndex(firstIndex, coll);
+ params.bounds.isSimpleRange = true;
+ params.bounds.startKey = objWithMinKey(1);
+ params.bounds.endKey = objWithMaxKey(1);
+ params.bounds.endKeyInclusive = true;
+ params.direction = 1;
+ ms->addChild(new IndexScan(&_txn, params, ws, NULL));
+
+ // b:1
+ params.descriptor = getIndex(secondIndex, coll);
+ ms->addChild(new IndexScan(&_txn, params, ws, NULL));
+
+ PlanExecutor* rawExec;
+ Status status = PlanExecutor::make(&_txn,
+ ws,
+ new FetchStage(&_txn, ws, ms, NULL, coll),
+ coll,
+ PlanExecutor::YIELD_MANUAL,
+ &rawExec);
+ ASSERT_OK(status);
+ std::unique_ptr<PlanExecutor> exec(rawExec);
+
+ for (int i = 0; i < N; ++i) {
+ BSONObj first, second;
+ ASSERT_EQUALS(PlanExecutor::ADVANCED, exec->getNext(&first, NULL));
+ ASSERT_EQUALS(PlanExecutor::ADVANCED, exec->getNext(&second, NULL));
+ ASSERT_EQUALS(first["c"].numberInt(), second["c"].numberInt());
+ ASSERT_EQUALS(i, first["c"].numberInt());
+ ASSERT((first.hasField("a") && second.hasField("b")) ||
+ (first.hasField("b") && second.hasField("a")));
}
- static const char* ns() { return "unittests.QueryStageMergeSort"; }
-
- protected:
- OperationContextImpl _txn;
-
- private:
- DBDirectClient _client;
- };
-
- // SERVER-1205:
- // find($or[{a:1}, {b:1}]).sort({c:1}) with indices {a:1, c:1} and {b:1, c:1}.
- class QueryStageMergeSortPrefixIndex : public QueryStageMergeSortTestBase {
- public:
- void run() {
- OldClientWriteContext ctx(&_txn, ns());
- Database* db = ctx.db();
- Collection* coll = db->getCollection(ns());
- if (!coll) {
- WriteUnitOfWork wuow(&_txn);
- coll = db->createCollection(&_txn, ns());
- wuow.commit();
- }
-
- const int N = 50;
-
- for (int i = 0; i < N; ++i) {
- insert(BSON("a" << 1 << "c" << i));
- insert(BSON("b" << 1 << "c" << i));
- }
-
- BSONObj firstIndex = BSON("a" << 1 << "c" << 1);
- BSONObj secondIndex = BSON("b" << 1 << "c" << 1);
-
- addIndex(firstIndex);
- addIndex(secondIndex);
-
- WorkingSet* ws = new WorkingSet();
- // Sort by c:1
- MergeSortStageParams msparams;
- msparams.pattern = BSON("c" << 1);
- MergeSortStage* ms = new MergeSortStage(msparams, ws, coll);
-
- // a:1
- IndexScanParams params;
- params.descriptor = getIndex(firstIndex, coll);
- params.bounds.isSimpleRange = true;
- params.bounds.startKey = objWithMinKey(1);
- params.bounds.endKey = objWithMaxKey(1);
- params.bounds.endKeyInclusive = true;
- params.direction = 1;
- ms->addChild(new IndexScan(&_txn, params, ws, NULL));
-
- // b:1
- params.descriptor = getIndex(secondIndex, coll);
- ms->addChild(new IndexScan(&_txn, params, ws, NULL));
-
- // Must fetch if we want to easily pull out an obj.
- PlanExecutor* rawExec;
- Status status = PlanExecutor::make(&_txn, ws, new FetchStage(&_txn, ws, ms, NULL, coll),
- coll, PlanExecutor::YIELD_MANUAL, &rawExec);
- ASSERT_OK(status);
- std::unique_ptr<PlanExecutor> exec(rawExec);
-
- for (int i = 0; i < N; ++i) {
- BSONObj first, second;
- ASSERT_EQUALS(PlanExecutor::ADVANCED, exec->getNext(&first, NULL));
- ASSERT_EQUALS(PlanExecutor::ADVANCED, exec->getNext(&second, NULL));
- ASSERT_EQUALS(first["c"].numberInt(), second["c"].numberInt());
- ASSERT_EQUALS(i, first["c"].numberInt());
- ASSERT((first.hasField("a") && second.hasField("b"))
- || (first.hasField("b") && second.hasField("a")));
- }
-
- // Should be done now.
- BSONObj foo;
- ASSERT_NOT_EQUALS(PlanExecutor::ADVANCED, exec->getNext(&foo, NULL));
+ // Should be done now.
+ BSONObj foo;
+ ASSERT_EQUALS(PlanExecutor::IS_EOF, exec->getNext(&foo, NULL));
+ }
+};
+
+// Each inserted document appears in both indices, no deduping, get each result twice.
+class QueryStageMergeSortDupsNoDedup : public QueryStageMergeSortTestBase {
+public:
+ void run() {
+ OldClientWriteContext ctx(&_txn, ns());
+ Database* db = ctx.db();
+ Collection* coll = db->getCollection(ns());
+ if (!coll) {
+ WriteUnitOfWork wuow(&_txn);
+ coll = db->createCollection(&_txn, ns());
+ wuow.commit();
}
- };
-
- // Each inserted document appears in both indices but is deduped and returned properly/sorted.
- class QueryStageMergeSortDups : public QueryStageMergeSortTestBase {
- public:
- void run() {
- OldClientWriteContext ctx(&_txn, ns());
- Database* db = ctx.db();
- Collection* coll = db->getCollection(ns());
- if (!coll) {
- WriteUnitOfWork wuow(&_txn);
- coll = db->createCollection(&_txn, ns());
- wuow.commit();
- }
-
- const int N = 50;
-
- for (int i = 0; i < N; ++i) {
- insert(BSON("a" << 1 << "b" << 1 << "c" << i));
- insert(BSON("a" << 1 << "b" << 1 << "c" << i));
- }
-
- BSONObj firstIndex = BSON("a" << 1 << "c" << 1);
- BSONObj secondIndex = BSON("b" << 1 << "c" << 1);
-
- addIndex(firstIndex);
- addIndex(secondIndex);
-
- WorkingSet* ws = new WorkingSet();
- // Sort by c:1
- MergeSortStageParams msparams;
- msparams.pattern = BSON("c" << 1);
- MergeSortStage* ms = new MergeSortStage(msparams, ws, coll);
-
- // a:1
- IndexScanParams params;
- params.descriptor = getIndex(firstIndex, coll);
- params.bounds.isSimpleRange = true;
- params.bounds.startKey = objWithMinKey(1);
- params.bounds.endKey = objWithMaxKey(1);
- params.bounds.endKeyInclusive = true;
- params.direction = 1;
- ms->addChild(new IndexScan(&_txn, params, ws, NULL));
- // b:1
- params.descriptor = getIndex(secondIndex, coll);
- ms->addChild(new IndexScan(&_txn, params, ws, NULL));
-
- PlanExecutor* rawExec;
- Status status = PlanExecutor::make(&_txn, ws, new FetchStage(&_txn, ws, ms, NULL, coll),
- coll, PlanExecutor::YIELD_MANUAL, &rawExec);
- ASSERT_OK(status);
- std::unique_ptr<PlanExecutor> exec(rawExec);
-
- for (int i = 0; i < N; ++i) {
- BSONObj first, second;
- ASSERT_EQUALS(PlanExecutor::ADVANCED, exec->getNext(&first, NULL));
- ASSERT_EQUALS(PlanExecutor::ADVANCED, exec->getNext(&second, NULL));
- ASSERT_EQUALS(first["c"].numberInt(), second["c"].numberInt());
- ASSERT_EQUALS(i, first["c"].numberInt());
- ASSERT((first.hasField("a") && second.hasField("b"))
- || (first.hasField("b") && second.hasField("a")));
- }
+ const int N = 50;
- // Should be done now.
- BSONObj foo;
- ASSERT_EQUALS(PlanExecutor::IS_EOF, exec->getNext(&foo, NULL));
+ for (int i = 0; i < N; ++i) {
+ insert(BSON("a" << 1 << "b" << 1 << "c" << i));
}
- };
-
- // Each inserted document appears in both indices, no deduping, get each result twice.
- class QueryStageMergeSortDupsNoDedup : public QueryStageMergeSortTestBase {
- public:
- void run() {
- OldClientWriteContext ctx(&_txn, ns());
- Database* db = ctx.db();
- Collection* coll = db->getCollection(ns());
- if (!coll) {
- WriteUnitOfWork wuow(&_txn);
- coll = db->createCollection(&_txn, ns());
- wuow.commit();
- }
-
- const int N = 50;
-
- for (int i = 0; i < N; ++i) {
- insert(BSON("a" << 1 << "b" << 1 << "c" << i));
- }
-
- BSONObj firstIndex = BSON("a" << 1 << "c" << 1);
- BSONObj secondIndex = BSON("b" << 1 << "c" << 1);
-
- addIndex(firstIndex);
- addIndex(secondIndex);
-
- WorkingSet* ws = new WorkingSet();
- // Sort by c:1
- MergeSortStageParams msparams;
- msparams.dedup = false;
- msparams.pattern = BSON("c" << 1);
- MergeSortStage* ms = new MergeSortStage(msparams, ws, coll);
-
- // a:1
- IndexScanParams params;
- params.descriptor = getIndex(firstIndex, coll);
- params.bounds.isSimpleRange = true;
- params.bounds.startKey = objWithMinKey(1);
- params.bounds.endKey = objWithMaxKey(1);
- params.bounds.endKeyInclusive = true;
- params.direction = 1;
- ms->addChild(new IndexScan(&_txn, params, ws, NULL));
-
- // b:1
- params.descriptor = getIndex(secondIndex, coll);
- ms->addChild(new IndexScan(&_txn, params, ws, NULL));
- PlanExecutor* rawExec;
- Status status = PlanExecutor::make(&_txn, ws, new FetchStage(&_txn, ws, ms, NULL, coll),
- coll, PlanExecutor::YIELD_MANUAL, &rawExec);
- ASSERT_OK(status);
- std::unique_ptr<PlanExecutor> exec(rawExec);
-
- for (int i = 0; i < N; ++i) {
- BSONObj first, second;
- // We inserted N objects but we get 2 * N from the runner because of dups.
- ASSERT_EQUALS(PlanExecutor::ADVANCED, exec->getNext(&first, NULL));
- ASSERT_EQUALS(PlanExecutor::ADVANCED, exec->getNext(&second, NULL));
- ASSERT_EQUALS(first["c"].numberInt(), second["c"].numberInt());
- ASSERT_EQUALS(i, first["c"].numberInt());
- ASSERT((first.hasField("a") && second.hasField("b"))
- || (first.hasField("b") && second.hasField("a")));
- }
+ BSONObj firstIndex = BSON("a" << 1 << "c" << 1);
+ BSONObj secondIndex = BSON("b" << 1 << "c" << 1);
+
+ addIndex(firstIndex);
+ addIndex(secondIndex);
+
+ WorkingSet* ws = new WorkingSet();
+ // Sort by c:1
+ MergeSortStageParams msparams;
+ msparams.dedup = false;
+ msparams.pattern = BSON("c" << 1);
+ MergeSortStage* ms = new MergeSortStage(msparams, ws, coll);
+
+ // a:1
+ IndexScanParams params;
+ params.descriptor = getIndex(firstIndex, coll);
+ params.bounds.isSimpleRange = true;
+ params.bounds.startKey = objWithMinKey(1);
+ params.bounds.endKey = objWithMaxKey(1);
+ params.bounds.endKeyInclusive = true;
+ params.direction = 1;
+ ms->addChild(new IndexScan(&_txn, params, ws, NULL));
+
+ // b:1
+ params.descriptor = getIndex(secondIndex, coll);
+ ms->addChild(new IndexScan(&_txn, params, ws, NULL));
+
+ PlanExecutor* rawExec;
+ Status status = PlanExecutor::make(&_txn,
+ ws,
+ new FetchStage(&_txn, ws, ms, NULL, coll),
+ coll,
+ PlanExecutor::YIELD_MANUAL,
+ &rawExec);
+ ASSERT_OK(status);
+ std::unique_ptr<PlanExecutor> exec(rawExec);
+
+ for (int i = 0; i < N; ++i) {
+ BSONObj first, second;
+ // We inserted N objects but we get 2 * N from the runner because of dups.
+ ASSERT_EQUALS(PlanExecutor::ADVANCED, exec->getNext(&first, NULL));
+ ASSERT_EQUALS(PlanExecutor::ADVANCED, exec->getNext(&second, NULL));
+ ASSERT_EQUALS(first["c"].numberInt(), second["c"].numberInt());
+ ASSERT_EQUALS(i, first["c"].numberInt());
+ ASSERT((first.hasField("a") && second.hasField("b")) ||
+ (first.hasField("b") && second.hasField("a")));
+ }
- // Should be done now.
- BSONObj foo;
- ASSERT_EQUALS(PlanExecutor::IS_EOF, exec->getNext(&foo, NULL));
+ // Should be done now.
+ BSONObj foo;
+ ASSERT_EQUALS(PlanExecutor::IS_EOF, exec->getNext(&foo, NULL));
+ }
+};
+
+// Decreasing indices merged ok. Basically the test above but decreasing.
+class QueryStageMergeSortPrefixIndexReverse : public QueryStageMergeSortTestBase {
+public:
+ void run() {
+ OldClientWriteContext ctx(&_txn, ns());
+ Database* db = ctx.db();
+ Collection* coll = db->getCollection(ns());
+ if (!coll) {
+ WriteUnitOfWork wuow(&_txn);
+ coll = db->createCollection(&_txn, ns());
+ wuow.commit();
}
- };
-
- // Decreasing indices merged ok. Basically the test above but decreasing.
- class QueryStageMergeSortPrefixIndexReverse : public QueryStageMergeSortTestBase {
- public:
- void run() {
- OldClientWriteContext ctx(&_txn, ns());
- Database* db = ctx.db();
- Collection* coll = db->getCollection(ns());
- if (!coll) {
- WriteUnitOfWork wuow(&_txn);
- coll = db->createCollection(&_txn, ns());
- wuow.commit();
- }
- const int N = 50;
+ const int N = 50;
- for (int i = 0; i < N; ++i) {
- // We insert a:1 c:i for i=0..49 but in reverse order for the heck of it.
- insert(BSON("a" << 1 << "c" << N - i - 1));
- insert(BSON("b" << 1 << "c" << i));
- }
+ for (int i = 0; i < N; ++i) {
+ // We insert a:1 c:i for i=0..49 but in reverse order for the heck of it.
+ insert(BSON("a" << 1 << "c" << N - i - 1));
+ insert(BSON("b" << 1 << "c" << i));
+ }
- BSONObj firstIndex = BSON("a" << 1 << "c" << -1);
- BSONObj secondIndex = BSON("b" << 1 << "c" << -1);
-
- addIndex(firstIndex);
- addIndex(secondIndex);
-
- WorkingSet* ws = new WorkingSet();
- // Sort by c:-1
- MergeSortStageParams msparams;
- msparams.pattern = BSON("c" << -1);
- MergeSortStage* ms = new MergeSortStage(msparams, ws, coll);
-
- // a:1
- IndexScanParams params;
- params.descriptor = getIndex(firstIndex, coll);
- params.bounds.isSimpleRange = true;
- params.bounds.startKey = objWithMaxKey(1);
- params.bounds.endKey = objWithMinKey(1);
- params.bounds.endKeyInclusive = true;
- // This is the direction along the index.
- params.direction = 1;
- ms->addChild(new IndexScan(&_txn, params, ws, NULL));
+ BSONObj firstIndex = BSON("a" << 1 << "c" << -1);
+ BSONObj secondIndex = BSON("b" << 1 << "c" << -1);
+
+ addIndex(firstIndex);
+ addIndex(secondIndex);
+
+ WorkingSet* ws = new WorkingSet();
+ // Sort by c:-1
+ MergeSortStageParams msparams;
+ msparams.pattern = BSON("c" << -1);
+ MergeSortStage* ms = new MergeSortStage(msparams, ws, coll);
+
+ // a:1
+ IndexScanParams params;
+ params.descriptor = getIndex(firstIndex, coll);
+ params.bounds.isSimpleRange = true;
+ params.bounds.startKey = objWithMaxKey(1);
+ params.bounds.endKey = objWithMinKey(1);
+ params.bounds.endKeyInclusive = true;
+ // This is the direction along the index.
+ params.direction = 1;
+ ms->addChild(new IndexScan(&_txn, params, ws, NULL));
+
+ // b:1
+ params.descriptor = getIndex(secondIndex, coll);
+ ms->addChild(new IndexScan(&_txn, params, ws, NULL));
+
+ PlanExecutor* rawExec;
+ Status status = PlanExecutor::make(&_txn,
+ ws,
+ new FetchStage(&_txn, ws, ms, NULL, coll),
+ coll,
+ PlanExecutor::YIELD_MANUAL,
+ &rawExec);
+ ASSERT_OK(status);
+ std::unique_ptr<PlanExecutor> exec(rawExec);
+
+ for (int i = 0; i < N; ++i) {
+ BSONObj first, second;
+ ASSERT_EQUALS(PlanExecutor::ADVANCED, exec->getNext(&first, NULL));
+ ASSERT_EQUALS(PlanExecutor::ADVANCED, exec->getNext(&second, NULL));
+ ASSERT_EQUALS(first["c"].numberInt(), second["c"].numberInt());
+ ASSERT_EQUALS(N - i - 1, first["c"].numberInt());
+ ASSERT((first.hasField("a") && second.hasField("b")) ||
+ (first.hasField("b") && second.hasField("a")));
+ }
- // b:1
- params.descriptor = getIndex(secondIndex, coll);
- ms->addChild(new IndexScan(&_txn, params, ws, NULL));
+ // Should be done now.
+ BSONObj foo;
+ ASSERT_EQUALS(PlanExecutor::IS_EOF, exec->getNext(&foo, NULL));
+ }
+};
+
+// One stage EOF immediately
+class QueryStageMergeSortOneStageEOF : public QueryStageMergeSortTestBase {
+public:
+ void run() {
+ OldClientWriteContext ctx(&_txn, ns());
+ Database* db = ctx.db();
+ Collection* coll = db->getCollection(ns());
+ if (!coll) {
+ WriteUnitOfWork wuow(&_txn);
+ coll = db->createCollection(&_txn, ns());
+ wuow.commit();
+ }
- PlanExecutor* rawExec;
- Status status = PlanExecutor::make(&_txn, ws, new FetchStage(&_txn, ws, ms, NULL, coll),
- coll, PlanExecutor::YIELD_MANUAL, &rawExec);
- ASSERT_OK(status);
- std::unique_ptr<PlanExecutor> exec(rawExec);
-
- for (int i = 0; i < N; ++i) {
- BSONObj first, second;
- ASSERT_EQUALS(PlanExecutor::ADVANCED, exec->getNext(&first, NULL));
- ASSERT_EQUALS(PlanExecutor::ADVANCED, exec->getNext(&second, NULL));
- ASSERT_EQUALS(first["c"].numberInt(), second["c"].numberInt());
- ASSERT_EQUALS(N - i - 1, first["c"].numberInt());
- ASSERT((first.hasField("a") && second.hasField("b"))
- || (first.hasField("b") && second.hasField("a")));
- }
+ const int N = 50;
- // Should be done now.
- BSONObj foo;
- ASSERT_EQUALS(PlanExecutor::IS_EOF, exec->getNext(&foo, NULL));
+ for (int i = 0; i < N; ++i) {
+ insert(BSON("a" << 1 << "c" << i));
+ insert(BSON("b" << 1 << "c" << i));
}
- };
-
- // One stage EOF immediately
- class QueryStageMergeSortOneStageEOF : public QueryStageMergeSortTestBase {
- public:
- void run() {
- OldClientWriteContext ctx(&_txn, ns());
- Database* db = ctx.db();
- Collection* coll = db->getCollection(ns());
- if (!coll) {
- WriteUnitOfWork wuow(&_txn);
- coll = db->createCollection(&_txn, ns());
- wuow.commit();
- }
-
- const int N = 50;
- for (int i = 0; i < N; ++i) {
- insert(BSON("a" << 1 << "c" << i));
- insert(BSON("b" << 1 << "c" << i));
- }
+ BSONObj firstIndex = BSON("a" << 1 << "c" << 1);
+ BSONObj secondIndex = BSON("b" << 1 << "c" << 1);
+
+ addIndex(firstIndex);
+ addIndex(secondIndex);
+
+ WorkingSet* ws = new WorkingSet();
+ // Sort by c:1
+ MergeSortStageParams msparams;
+ msparams.pattern = BSON("c" << 1);
+ MergeSortStage* ms = new MergeSortStage(msparams, ws, coll);
+
+ // a:1
+ IndexScanParams params;
+ params.descriptor = getIndex(firstIndex, coll);
+ params.bounds.isSimpleRange = true;
+ params.bounds.startKey = objWithMinKey(1);
+ params.bounds.endKey = objWithMaxKey(1);
+ params.bounds.endKeyInclusive = true;
+ params.direction = 1;
+ ms->addChild(new IndexScan(&_txn, params, ws, NULL));
+
+ // b:51 (EOF)
+ params.descriptor = getIndex(secondIndex, coll);
+ params.bounds.startKey = BSON("" << 51 << "" << MinKey);
+ params.bounds.endKey = BSON("" << 51 << "" << MaxKey);
+ ms->addChild(new IndexScan(&_txn, params, ws, NULL));
+
+ PlanExecutor* rawExec;
+ Status status = PlanExecutor::make(&_txn,
+ ws,
+ new FetchStage(&_txn, ws, ms, NULL, coll),
+ coll,
+ PlanExecutor::YIELD_MANUAL,
+ &rawExec);
+ ASSERT_OK(status);
+ std::unique_ptr<PlanExecutor> exec(rawExec);
+
+ // Only getting results from the a:1 index scan.
+ for (int i = 0; i < N; ++i) {
+ BSONObj obj;
+ ASSERT_EQUALS(PlanExecutor::ADVANCED, exec->getNext(&obj, NULL));
+ ASSERT_EQUALS(i, obj["c"].numberInt());
+ ASSERT_EQUALS(1, obj["a"].numberInt());
+ }
- BSONObj firstIndex = BSON("a" << 1 << "c" << 1);
- BSONObj secondIndex = BSON("b" << 1 << "c" << 1);
-
- addIndex(firstIndex);
- addIndex(secondIndex);
-
- WorkingSet* ws = new WorkingSet();
- // Sort by c:1
- MergeSortStageParams msparams;
- msparams.pattern = BSON("c" << 1);
- MergeSortStage* ms = new MergeSortStage(msparams, ws, coll);
-
- // a:1
- IndexScanParams params;
- params.descriptor = getIndex(firstIndex, coll);
- params.bounds.isSimpleRange = true;
- params.bounds.startKey = objWithMinKey(1);
- params.bounds.endKey = objWithMaxKey(1);
- params.bounds.endKeyInclusive = true;
- params.direction = 1;
- ms->addChild(new IndexScan(&_txn, params, ws, NULL));
+ // Should be done now.
+ BSONObj foo;
+ ASSERT_EQUALS(PlanExecutor::IS_EOF, exec->getNext(&foo, NULL));
+ }
+};
+
+// N stages each have 1 result
+class QueryStageMergeSortManyShort : public QueryStageMergeSortTestBase {
+public:
+ void run() {
+ OldClientWriteContext ctx(&_txn, ns());
+ Database* db = ctx.db();
+ Collection* coll = db->getCollection(ns());
+ if (!coll) {
+ WriteUnitOfWork wuow(&_txn);
+ coll = db->createCollection(&_txn, ns());
+ wuow.commit();
+ }
- // b:51 (EOF)
- params.descriptor = getIndex(secondIndex, coll);
- params.bounds.startKey = BSON("" << 51 << "" << MinKey);
- params.bounds.endKey = BSON("" << 51 << "" << MaxKey);
+ WorkingSet* ws = new WorkingSet();
+ // Sort by foo:1
+ MergeSortStageParams msparams;
+ msparams.pattern = BSON("foo" << 1);
+ MergeSortStage* ms = new MergeSortStage(msparams, ws, coll);
+
+ IndexScanParams params;
+ params.bounds.isSimpleRange = true;
+ params.bounds.startKey = objWithMinKey(1);
+ params.bounds.endKey = objWithMaxKey(1);
+ params.bounds.endKeyInclusive = true;
+ params.direction = 1;
+
+ int numIndices = 20;
+ for (int i = 0; i < numIndices; ++i) {
+ // 'a', 'b', ...
+ string index(1, 'a' + i);
+ insert(BSON(index << 1 << "foo" << i));
+
+ BSONObj indexSpec = BSON(index << 1 << "foo" << 1);
+ addIndex(indexSpec);
+ params.descriptor = getIndex(indexSpec, coll);
ms->addChild(new IndexScan(&_txn, params, ws, NULL));
-
- PlanExecutor* rawExec;
- Status status = PlanExecutor::make(&_txn, ws, new FetchStage(&_txn, ws, ms, NULL, coll),
- coll, PlanExecutor::YIELD_MANUAL, &rawExec);
- ASSERT_OK(status);
- std::unique_ptr<PlanExecutor> exec(rawExec);
-
- // Only getting results from the a:1 index scan.
- for (int i = 0; i < N; ++i) {
- BSONObj obj;
- ASSERT_EQUALS(PlanExecutor::ADVANCED, exec->getNext(&obj, NULL));
- ASSERT_EQUALS(i, obj["c"].numberInt());
- ASSERT_EQUALS(1, obj["a"].numberInt());
- }
-
- // Should be done now.
- BSONObj foo;
- ASSERT_EQUALS(PlanExecutor::IS_EOF, exec->getNext(&foo, NULL));
}
- };
-
- // N stages each have 1 result
- class QueryStageMergeSortManyShort : public QueryStageMergeSortTestBase {
- public:
- void run() {
- OldClientWriteContext ctx(&_txn, ns());
- Database* db = ctx.db();
- Collection* coll = db->getCollection(ns());
- if (!coll) {
- WriteUnitOfWork wuow(&_txn);
- coll = db->createCollection(&_txn, ns());
- wuow.commit();
- }
- WorkingSet* ws = new WorkingSet();
- // Sort by foo:1
- MergeSortStageParams msparams;
- msparams.pattern = BSON("foo" << 1);
- MergeSortStage* ms = new MergeSortStage(msparams, ws, coll);
-
- IndexScanParams params;
- params.bounds.isSimpleRange = true;
- params.bounds.startKey = objWithMinKey(1);
- params.bounds.endKey = objWithMaxKey(1);
- params.bounds.endKeyInclusive = true;
- params.direction = 1;
-
- int numIndices = 20;
- for (int i = 0; i < numIndices; ++i) {
- // 'a', 'b', ...
- string index(1, 'a' + i);
- insert(BSON(index << 1 << "foo" << i));
-
- BSONObj indexSpec = BSON(index << 1 << "foo" << 1);
- addIndex(indexSpec);
- params.descriptor = getIndex(indexSpec, coll);
- ms->addChild(new IndexScan(&_txn, params, ws, NULL));
- }
-
- PlanExecutor* rawExec;
- Status status = PlanExecutor::make(&_txn, ws, new FetchStage(&_txn, ws, ms, NULL, coll),
- coll, PlanExecutor::YIELD_MANUAL, &rawExec);
- ASSERT_OK(status);
- std::unique_ptr<PlanExecutor> exec(rawExec);
-
- for (int i = 0; i < numIndices; ++i) {
- BSONObj obj;
- ASSERT_EQUALS(PlanExecutor::ADVANCED, exec->getNext(&obj, NULL));
- ASSERT_EQUALS(i, obj["foo"].numberInt());
- string index(1, 'a' + i);
- ASSERT_EQUALS(1, obj[index].numberInt());
- }
-
- // Should be done now.
- BSONObj foo;
- ASSERT_EQUALS(PlanExecutor::IS_EOF, exec->getNext(&foo, NULL));
+ PlanExecutor* rawExec;
+ Status status = PlanExecutor::make(&_txn,
+ ws,
+ new FetchStage(&_txn, ws, ms, NULL, coll),
+ coll,
+ PlanExecutor::YIELD_MANUAL,
+ &rawExec);
+ ASSERT_OK(status);
+ std::unique_ptr<PlanExecutor> exec(rawExec);
+
+ for (int i = 0; i < numIndices; ++i) {
+ BSONObj obj;
+ ASSERT_EQUALS(PlanExecutor::ADVANCED, exec->getNext(&obj, NULL));
+ ASSERT_EQUALS(i, obj["foo"].numberInt());
+ string index(1, 'a' + i);
+ ASSERT_EQUALS(1, obj[index].numberInt());
}
- };
-
- // Invalidation mid-run
- class QueryStageMergeSortInvalidation : public QueryStageMergeSortTestBase {
- public:
- void run() {
- OldClientWriteContext ctx(&_txn, ns());
- Database* db = ctx.db();
- Collection* coll = db->getCollection(ns());
- if (!coll) {
- WriteUnitOfWork wuow(&_txn);
- coll = db->createCollection(&_txn, ns());
- wuow.commit();
- }
- WorkingSet ws;
- // Sort by foo:1
- MergeSortStageParams msparams;
- msparams.pattern = BSON("foo" << 1);
- unique_ptr<MergeSortStage> ms(new MergeSortStage(msparams, &ws, coll));
-
- IndexScanParams params;
- params.bounds.isSimpleRange = true;
- params.bounds.startKey = objWithMinKey(1);
- params.bounds.endKey = objWithMaxKey(1);
- params.bounds.endKeyInclusive = true;
- params.direction = 1;
-
- // Index 'a'+i has foo equal to 'i'.
-
- int numIndices = 20;
- for (int i = 0; i < numIndices; ++i) {
- // 'a', 'b', ...
- string index(1, 'a' + i);
- insert(BSON(index << 1 << "foo" << i));
-
- BSONObj indexSpec = BSON(index << 1 << "foo" << 1);
- addIndex(indexSpec);
- params.descriptor = getIndex(indexSpec, coll);
- ms->addChild(new IndexScan(&_txn, params, &ws, NULL));
- }
+ // Should be done now.
+ BSONObj foo;
+ ASSERT_EQUALS(PlanExecutor::IS_EOF, exec->getNext(&foo, NULL));
+ }
+};
+
+// Invalidation mid-run
+class QueryStageMergeSortInvalidation : public QueryStageMergeSortTestBase {
+public:
+ void run() {
+ OldClientWriteContext ctx(&_txn, ns());
+ Database* db = ctx.db();
+ Collection* coll = db->getCollection(ns());
+ if (!coll) {
+ WriteUnitOfWork wuow(&_txn);
+ coll = db->createCollection(&_txn, ns());
+ wuow.commit();
+ }
- set<RecordId> locs;
- getLocs(&locs, coll);
+ WorkingSet ws;
+ // Sort by foo:1
+ MergeSortStageParams msparams;
+ msparams.pattern = BSON("foo" << 1);
+ unique_ptr<MergeSortStage> ms(new MergeSortStage(msparams, &ws, coll));
+
+ IndexScanParams params;
+ params.bounds.isSimpleRange = true;
+ params.bounds.startKey = objWithMinKey(1);
+ params.bounds.endKey = objWithMaxKey(1);
+ params.bounds.endKeyInclusive = true;
+ params.direction = 1;
+
+ // Index 'a'+i has foo equal to 'i'.
+
+ int numIndices = 20;
+ for (int i = 0; i < numIndices; ++i) {
+ // 'a', 'b', ...
+ string index(1, 'a' + i);
+ insert(BSON(index << 1 << "foo" << i));
+
+ BSONObj indexSpec = BSON(index << 1 << "foo" << 1);
+ addIndex(indexSpec);
+ params.descriptor = getIndex(indexSpec, coll);
+ ms->addChild(new IndexScan(&_txn, params, &ws, NULL));
+ }
- set<RecordId>::iterator it = locs.begin();
+ set<RecordId> locs;
+ getLocs(&locs, coll);
- // Get 10 results. Should be getting results in order of 'locs'.
- int count = 0;
- while (!ms->isEOF() && count < 10) {
- WorkingSetID id = WorkingSet::INVALID_ID;
- PlanStage::StageState status = ms->work(&id);
- if (PlanStage::ADVANCED != status) { continue; }
+ set<RecordId>::iterator it = locs.begin();
- WorkingSetMember* member = ws.get(id);
- ASSERT_EQUALS(member->loc, *it);
- BSONElement elt;
- string index(1, 'a' + count);
- ASSERT(member->getFieldDotted(index, &elt));
- ASSERT_EQUALS(1, elt.numberInt());
- ASSERT(member->getFieldDotted("foo", &elt));
- ASSERT_EQUALS(count, elt.numberInt());
- ++count;
- ++it;
+ // Get 10 results. Should be getting results in order of 'locs'.
+ int count = 0;
+ while (!ms->isEOF() && count < 10) {
+ WorkingSetID id = WorkingSet::INVALID_ID;
+ PlanStage::StageState status = ms->work(&id);
+ if (PlanStage::ADVANCED != status) {
+ continue;
}
- // Invalidate locs[11]. Should force a fetch. We don't get it back.
- ms->saveState();
- ms->invalidate(&_txn, *it, INVALIDATION_DELETION);
- ms->restoreState(&_txn);
+ WorkingSetMember* member = ws.get(id);
+ ASSERT_EQUALS(member->loc, *it);
+ BSONElement elt;
+ string index(1, 'a' + count);
+ ASSERT(member->getFieldDotted(index, &elt));
+ ASSERT_EQUALS(1, elt.numberInt());
+ ASSERT(member->getFieldDotted("foo", &elt));
+ ASSERT_EQUALS(count, elt.numberInt());
+ ++count;
+ ++it;
+ }
+
+ // Invalidate locs[11]. Should force a fetch. We don't get it back.
+ ms->saveState();
+ ms->invalidate(&_txn, *it, INVALIDATION_DELETION);
+ ms->restoreState(&_txn);
- // Make sure locs[11] was fetched for us.
- {
+ // Make sure locs[11] was fetched for us.
+ {
// TODO: If we have "return upon invalidation" ever triggerable, do the following test.
/*
WorkingSetID id = WorkingSet::INVALID_ID;
@@ -600,46 +626,47 @@ namespace QueryStageMergeSortTests {
ASSERT_EQUALS(count, elt.numberInt());
*/
- ++it;
- ++count;
- }
-
- // And get the rest.
- while (!ms->isEOF()) {
- WorkingSetID id = WorkingSet::INVALID_ID;
- PlanStage::StageState status = ms->work(&id);
- if (PlanStage::ADVANCED != status) { continue; }
+ ++it;
+ ++count;
+ }
- WorkingSetMember* member = ws.get(id);
- ASSERT_EQUALS(member->loc, *it);
- BSONElement elt;
- string index(1, 'a' + count);
- ASSERT_TRUE(member->getFieldDotted(index, &elt));
- ASSERT_EQUALS(1, elt.numberInt());
- ASSERT(member->getFieldDotted("foo", &elt));
- ASSERT_EQUALS(count, elt.numberInt());
- ++count;
- ++it;
+ // And get the rest.
+ while (!ms->isEOF()) {
+ WorkingSetID id = WorkingSet::INVALID_ID;
+ PlanStage::StageState status = ms->work(&id);
+ if (PlanStage::ADVANCED != status) {
+ continue;
}
- }
- };
-
- class All : public Suite {
- public:
- All() : Suite( "query_stage_merge_sort_test" ) { }
-
- void setupTests() {
- add<QueryStageMergeSortPrefixIndex>();
- add<QueryStageMergeSortDups>();
- add<QueryStageMergeSortDupsNoDedup>();
- add<QueryStageMergeSortPrefixIndexReverse>();
- add<QueryStageMergeSortOneStageEOF>();
- add<QueryStageMergeSortManyShort>();
- add<QueryStageMergeSortInvalidation>();
- }
- };
- SuiteInstance<All> queryStageMergeSortTest;
+ WorkingSetMember* member = ws.get(id);
+ ASSERT_EQUALS(member->loc, *it);
+ BSONElement elt;
+ string index(1, 'a' + count);
+ ASSERT_TRUE(member->getFieldDotted(index, &elt));
+ ASSERT_EQUALS(1, elt.numberInt());
+ ASSERT(member->getFieldDotted("foo", &elt));
+ ASSERT_EQUALS(count, elt.numberInt());
+ ++count;
+ ++it;
+ }
+ }
+};
+
+class All : public Suite {
+public:
+ All() : Suite("query_stage_merge_sort_test") {}
+
+ void setupTests() {
+ add<QueryStageMergeSortPrefixIndex>();
+ add<QueryStageMergeSortDups>();
+ add<QueryStageMergeSortDupsNoDedup>();
+ add<QueryStageMergeSortPrefixIndexReverse>();
+ add<QueryStageMergeSortOneStageEOF>();
+ add<QueryStageMergeSortManyShort>();
+ add<QueryStageMergeSortInvalidation>();
+ }
+};
+
+SuiteInstance<All> queryStageMergeSortTest;
} // namespace
-