Skip to content

Commit ec236e8

Browse files
committed
SERVER-38227 Fix biggie unique index cursor with non-inclusive bound
Also add unittest to generic sorted data interface testing.
1 parent f99f0d9 commit ec236e8

File tree

2 files changed

+91
-7
lines changed

2 files changed

+91
-7
lines changed

src/mongo/db/storage/biggie/biggie_sorted_impl.cpp

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -94,12 +94,11 @@ std::string createKeyString(const BSONObj& key,
9494
b.append("", prefixToUse); // prefix
9595
b.append("", std::string(ks.getBuffer(), ks.getSize())); // key
9696

97-
std::unique_ptr<KeyString> retKs;
9897
if (isUnique)
99-
retKs = std::make_unique<KeyString>(version, b.obj(), allAscending);
98+
ks.resetToKey(b.obj(), allAscending);
10099
else
101-
retKs = std::make_unique<KeyString>(version, b.obj(), allAscending, loc);
102-
return std::string(retKs->getBuffer(), retKs->getSize());
100+
ks.resetToKey(b.obj(), allAscending, loc);
101+
return std::string(ks.getBuffer(), ks.getSize());
103102
}
104103

105104
bool keysAreIdentical(std::string ks1, std::string ks2, bool isUnique) {
@@ -688,13 +687,20 @@ boost::optional<IndexKeyEntry> SortedDataInterface::Cursor::seekAfterProcessing(
688687
} else {
689688
// Otherwise, we seek to the nearest element to our key, but only to the right.
690689
if (_forward) {
691-
_forwardIt = _workingCopy->lower_bound(workingCopyBound);
690+
if (inclusive)
691+
_forwardIt = _workingCopy->lower_bound(workingCopyBound);
692+
else
693+
_forwardIt = _workingCopy->upper_bound(workingCopyBound);
692694
} else {
693695
// Reverse iterators work with upper bound since upper bound will return the first
694696
// element past the argument, so when it becomes a reverse iterator, it goes
695697
// backwards one, (according to the C++ standard) and we end up in the right place.
696-
_reverseIt =
697-
StringStore::const_reverse_iterator(_workingCopy->upper_bound(workingCopyBound));
698+
if (inclusive)
699+
_reverseIt = StringStore::const_reverse_iterator(
700+
_workingCopy->upper_bound(workingCopyBound));
701+
else
702+
_reverseIt = StringStore::const_reverse_iterator(
703+
_workingCopy->lower_bound(workingCopyBound));
698704
}
699705
// Once again, we check to make sure the iterator didn't fall off the data structure and
700706
// still is in the ident.

src/mongo/db/storage/sorted_data_interface_test_cursor.cpp

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232

3333
#include "mongo/db/storage/sorted_data_interface_test_harness.h"
3434

35+
#include <algorithm>
3536
#include <memory>
3637

3738
#include "mongo/db/storage/sorted_data_interface.h"
@@ -168,5 +169,82 @@ TEST(SortedDataInterface, ExhaustCursorReversed) {
168169
}
169170
}
170171

172+
void testBoundaries(bool unique, bool forward, bool inclusive) {
173+
const auto harnessHelper(newSortedDataInterfaceHarnessHelper());
174+
const std::unique_ptr<SortedDataInterface> sorted(
175+
harnessHelper->newSortedDataInterface(unique));
176+
177+
const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
178+
ASSERT(sorted->isEmpty(opCtx.get()));
179+
180+
int nToInsert = 10;
181+
for (int i = 0; i < nToInsert; i++) {
182+
WriteUnitOfWork uow(opCtx.get());
183+
BSONObj key = BSON("" << i);
184+
RecordId loc(42 + i * 2);
185+
ASSERT_OK(sorted->insert(opCtx.get(), key, loc, true));
186+
uow.commit();
187+
}
188+
189+
{
190+
const std::unique_ptr<SortedDataInterface::Cursor> cursor(
191+
sorted->newCursor(opCtx.get(), forward));
192+
int startVal = 2;
193+
int endVal = 6;
194+
if (!forward)
195+
std::swap(startVal, endVal);
196+
197+
auto startKey = BSON("" << startVal);
198+
auto endKey = BSON("" << endVal);
199+
cursor->setEndPosition(endKey, inclusive);
200+
201+
auto entry = cursor->seek(startKey, inclusive);
202+
203+
// Check that the cursor returns the expected values in range.
204+
int step = forward ? 1 : -1;
205+
for (int i = startVal + (inclusive ? 0 : step); i != endVal + (inclusive ? step : 0);
206+
i += step) {
207+
ASSERT_EQ(entry, IndexKeyEntry(BSON("" << i), RecordId(42 + i * 2)));
208+
entry = cursor->next();
209+
}
210+
ASSERT(!entry);
211+
212+
// Cursor at EOF should remain at EOF when advanced
213+
ASSERT(!cursor->next());
214+
}
215+
}
216+
217+
TEST(SortedDataInterfaceBoundaryTest, UniqueForwardWithNonInclusiveBoundaries) {
218+
testBoundaries(/*unique*/ true, /*forward*/ true, /*inclusive*/ false);
219+
}
220+
221+
TEST(SortedDataInterfaceBoundaryTest, NonUniqueForwardWithNonInclusiveBoundaries) {
222+
testBoundaries(/*unique*/ false, /*forward*/ true, /*inclusive*/ false);
223+
}
224+
225+
TEST(SortedDataInterfaceBoundaryTest, UniqueForwardWithInclusiveBoundaries) {
226+
testBoundaries(/*unique*/ true, /*forward*/ true, /*inclusive*/ true);
227+
}
228+
229+
TEST(SortedDataInterfaceBoundaryTest, NonUniqueForwardWithInclusiveBoundaries) {
230+
testBoundaries(/*unique*/ false, /*forward*/ true, /*inclusive*/ true);
231+
}
232+
233+
TEST(SortedDataInterfaceBoundaryTest, UniqueBackwardWithNonInclusiveBoundaries) {
234+
testBoundaries(/*unique*/ true, /*forward*/ false, /*inclusive*/ false);
235+
}
236+
237+
TEST(SortedDataInterfaceBoundaryTest, NonUniqueBackwardWithNonInclusiveBoundaries) {
238+
testBoundaries(/*unique*/ false, /*forward*/ false, /*inclusive*/ false);
239+
}
240+
241+
TEST(SortedDataInterfaceBoundaryTest, UniqueBackwardWithInclusiveBoundaries) {
242+
testBoundaries(/*unique*/ true, /*forward*/ false, /*inclusive*/ true);
243+
}
244+
245+
TEST(SortedDataInterfaceBoundaryTest, NonUniqueBackwardWithInclusiveBoundaries) {
246+
testBoundaries(/*unique*/ false, /*forward*/ false, /*inclusive*/ true);
247+
}
248+
171249
} // namespace
172250
} // namespace mongo

0 commit comments

Comments
 (0)