1 // sorted_data_interface_test_cursor_locate.cpp
2 
3 
4 /**
5  *    Copyright (C) 2018-present MongoDB, Inc.
6  *
7  *    This program is free software: you can redistribute it and/or modify
8  *    it under the terms of the Server Side Public License, version 1,
9  *    as published by MongoDB, Inc.
10  *
11  *    This program is distributed in the hope that it will be useful,
12  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *    Server Side Public License for more details.
15  *
16  *    You should have received a copy of the Server Side Public License
17  *    along with this program. If not, see
18  *    <http://www.mongodb.com/licensing/server-side-public-license>.
19  *
20  *    As a special exception, the copyright holders give permission to link the
21  *    code of portions of this program with the OpenSSL library under certain
22  *    conditions as described in each individual source file and distribute
23  *    linked combinations including the program with the OpenSSL library. You
24  *    must comply with the Server Side Public License in all respects for
25  *    all of the code used other than as permitted herein. If you modify file(s)
26  *    with this exception, you may extend this exception to your version of the
27  *    file(s), but you are not obligated to do so. If you do not wish to do so,
28  *    delete this exception statement from your version. If you delete this
29  *    exception statement from all source files in the program, then also delete
30  *    it in the license file.
31  */
32 
33 #include "mongo/db/storage/sorted_data_interface_test_harness.h"
34 
35 #include <memory>
36 
37 #include "mongo/db/storage/sorted_data_interface.h"
38 #include "mongo/unittest/unittest.h"
39 
40 namespace mongo {
41 namespace {
42 
43 // Insert a key and try to locate it using a forward cursor
44 // by specifying its exact key and RecordId.
TEST(SortedDataInterface,Locate)45 TEST(SortedDataInterface, Locate) {
46     const auto harnessHelper(newSortedDataInterfaceHarnessHelper());
47     const std::unique_ptr<SortedDataInterface> sorted(harnessHelper->newSortedDataInterface(false));
48 
49     {
50         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
51         const std::unique_ptr<SortedDataInterface::Cursor> cursor(sorted->newCursor(opCtx.get()));
52         ASSERT(!cursor->seek(key1, true));
53     }
54 
55     {
56         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
57         {
58             WriteUnitOfWork uow(opCtx.get());
59             ASSERT_OK(sorted->insert(opCtx.get(), key1, loc1, true));
60             uow.commit();
61         }
62     }
63 
64     {
65         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
66         const std::unique_ptr<SortedDataInterface::Cursor> cursor(sorted->newCursor(opCtx.get()));
67 
68         ASSERT_EQ(cursor->seek(key1, true), IndexKeyEntry(key1, loc1));
69         ASSERT_EQ(cursor->next(), boost::none);
70     }
71 }
72 
73 // Insert a key and try to locate it using a reverse cursor
74 // by specifying its exact key and RecordId.
TEST(SortedDataInterface,LocateReversed)75 TEST(SortedDataInterface, LocateReversed) {
76     const auto harnessHelper(newSortedDataInterfaceHarnessHelper());
77     const std::unique_ptr<SortedDataInterface> sorted(harnessHelper->newSortedDataInterface(false));
78 
79     {
80         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
81         const std::unique_ptr<SortedDataInterface::Cursor> cursor(
82             sorted->newCursor(opCtx.get(), false));
83         ASSERT(!cursor->seek(key1, true));
84     }
85 
86     {
87         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
88         {
89             WriteUnitOfWork uow(opCtx.get());
90             ASSERT_OK(sorted->insert(opCtx.get(), key1, loc1, true));
91             uow.commit();
92         }
93     }
94 
95     {
96         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
97         const std::unique_ptr<SortedDataInterface::Cursor> cursor(
98             sorted->newCursor(opCtx.get(), false));
99 
100         ASSERT_EQ(cursor->seek(key1, true), IndexKeyEntry(key1, loc1));
101         ASSERT_EQ(cursor->next(), boost::none);
102     }
103 }
104 
105 // Insert a compound key and try to locate it using a forward cursor
106 // by specifying its exact key and RecordId.
TEST(SortedDataInterface,LocateCompoundKey)107 TEST(SortedDataInterface, LocateCompoundKey) {
108     const auto harnessHelper(newSortedDataInterfaceHarnessHelper());
109     const std::unique_ptr<SortedDataInterface> sorted(harnessHelper->newSortedDataInterface(false));
110 
111     {
112         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
113         const std::unique_ptr<SortedDataInterface::Cursor> cursor(sorted->newCursor(opCtx.get()));
114         ASSERT(!cursor->seek(compoundKey1a, true));
115     }
116 
117     {
118         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
119         {
120             WriteUnitOfWork uow(opCtx.get());
121             ASSERT_OK(sorted->insert(opCtx.get(), compoundKey1a, loc1, true));
122             uow.commit();
123         }
124     }
125 
126     {
127         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
128         const std::unique_ptr<SortedDataInterface::Cursor> cursor(sorted->newCursor(opCtx.get()));
129 
130         ASSERT_EQ(cursor->seek(compoundKey1a, true), IndexKeyEntry(compoundKey1a, loc1));
131         ASSERT_EQ(cursor->next(), boost::none);
132     }
133 }
134 
135 // Insert a compound key and try to locate it using a reverse cursor
136 // by specifying its exact key and RecordId.
TEST(SortedDataInterface,LocateCompoundKeyReversed)137 TEST(SortedDataInterface, LocateCompoundKeyReversed) {
138     const auto harnessHelper(newSortedDataInterfaceHarnessHelper());
139     const std::unique_ptr<SortedDataInterface> sorted(harnessHelper->newSortedDataInterface(false));
140 
141     {
142         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
143         const std::unique_ptr<SortedDataInterface::Cursor> cursor(
144             sorted->newCursor(opCtx.get(), false));
145         ASSERT(!cursor->seek(compoundKey1a, true));
146     }
147 
148     {
149         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
150         {
151             WriteUnitOfWork uow(opCtx.get());
152             ASSERT_OK(sorted->insert(opCtx.get(), compoundKey1a, loc1, true));
153             uow.commit();
154         }
155     }
156 
157     {
158         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
159         const std::unique_ptr<SortedDataInterface::Cursor> cursor(
160             sorted->newCursor(opCtx.get(), false));
161 
162         ASSERT_EQ(cursor->seek(compoundKey1a, true), IndexKeyEntry(compoundKey1a, loc1));
163         ASSERT_EQ(cursor->next(), boost::none);
164     }
165 }
166 
167 // Insert multiple keys and try to locate them using a forward cursor
168 // by specifying their exact key and RecordId.
TEST(SortedDataInterface,LocateMultiple)169 TEST(SortedDataInterface, LocateMultiple) {
170     const auto harnessHelper(newSortedDataInterfaceHarnessHelper());
171     const std::unique_ptr<SortedDataInterface> sorted(harnessHelper->newSortedDataInterface(false));
172 
173     {
174         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
175         const std::unique_ptr<SortedDataInterface::Cursor> cursor(sorted->newCursor(opCtx.get()));
176         ASSERT(!cursor->seek(key1, true));
177     }
178 
179     {
180         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
181         {
182             WriteUnitOfWork uow(opCtx.get());
183             ASSERT_OK(sorted->insert(opCtx.get(), key1, loc1, true));
184             ASSERT_OK(sorted->insert(opCtx.get(), key2, loc2, true));
185             uow.commit();
186         }
187     }
188 
189     {
190         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
191         const std::unique_ptr<SortedDataInterface::Cursor> cursor(sorted->newCursor(opCtx.get()));
192 
193         ASSERT_EQ(cursor->seek(key1, true), IndexKeyEntry(key1, loc1));
194         ASSERT_EQ(cursor->next(), IndexKeyEntry(key2, loc2));
195         ASSERT_EQ(cursor->next(), boost::none);
196     }
197 
198     {
199         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
200         {
201             WriteUnitOfWork uow(opCtx.get());
202             ASSERT_OK(sorted->insert(opCtx.get(), key3, loc3, true));
203             uow.commit();
204         }
205     }
206 
207     {
208         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
209         const std::unique_ptr<SortedDataInterface::Cursor> cursor(sorted->newCursor(opCtx.get()));
210 
211         ASSERT_EQ(cursor->seek(key2, true), IndexKeyEntry(key2, loc2));
212         ASSERT_EQ(cursor->next(), IndexKeyEntry(key3, loc3));
213         ASSERT_EQ(cursor->next(), boost::none);
214 
215         ASSERT_EQ(cursor->seek(key1, true), IndexKeyEntry(key1, loc1));
216         ASSERT_EQ(cursor->next(), IndexKeyEntry(key2, loc2));
217         ASSERT_EQ(cursor->next(), IndexKeyEntry(key3, loc3));
218         ASSERT_EQ(cursor->next(), boost::none);
219     }
220 }
221 
222 // Insert multiple keys and try to locate them using a reverse cursor
223 // by specifying their exact key and RecordId.
TEST(SortedDataInterface,LocateMultipleReversed)224 TEST(SortedDataInterface, LocateMultipleReversed) {
225     const auto harnessHelper(newSortedDataInterfaceHarnessHelper());
226     const std::unique_ptr<SortedDataInterface> sorted(harnessHelper->newSortedDataInterface(false));
227 
228     {
229         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
230         const std::unique_ptr<SortedDataInterface::Cursor> cursor(
231             sorted->newCursor(opCtx.get(), false));
232         ASSERT(!cursor->seek(key3, true));
233     }
234 
235     {
236         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
237         {
238             WriteUnitOfWork uow(opCtx.get());
239             ASSERT_OK(sorted->insert(opCtx.get(), key1, loc1, true));
240             ASSERT_OK(sorted->insert(opCtx.get(), key2, loc2, true));
241             uow.commit();
242         }
243     }
244 
245     {
246         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
247         const std::unique_ptr<SortedDataInterface::Cursor> cursor(
248             sorted->newCursor(opCtx.get(), false));
249 
250         ASSERT_EQ(cursor->seek(key2, true), IndexKeyEntry(key2, loc2));
251         ASSERT_EQ(cursor->next(), IndexKeyEntry(key1, loc1));
252         ASSERT_EQ(cursor->next(), boost::none);
253     }
254 
255     {
256         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
257         {
258             WriteUnitOfWork uow(opCtx.get());
259             ASSERT_OK(sorted->insert(opCtx.get(), key3, loc3, true));
260             uow.commit();
261         }
262     }
263 
264     {
265         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
266         const std::unique_ptr<SortedDataInterface::Cursor> cursor(
267             sorted->newCursor(opCtx.get(), false));
268 
269         ASSERT_EQ(cursor->seek(key2, true), IndexKeyEntry(key2, loc2));
270         ASSERT_EQ(cursor->next(), IndexKeyEntry(key1, loc1));
271         ASSERT_EQ(cursor->next(), boost::none);
272 
273         ASSERT_EQ(cursor->seek(key3, true), IndexKeyEntry(key3, loc3));
274         ASSERT_EQ(cursor->next(), IndexKeyEntry(key2, loc2));
275         ASSERT_EQ(cursor->next(), IndexKeyEntry(key1, loc1));
276         ASSERT_EQ(cursor->next(), boost::none);
277     }
278 }
279 
280 // Insert multiple compound keys and try to locate them using a forward cursor
281 // by specifying their exact key and RecordId.
TEST(SortedDataInterface,LocateMultipleCompoundKeys)282 TEST(SortedDataInterface, LocateMultipleCompoundKeys) {
283     const auto harnessHelper(newSortedDataInterfaceHarnessHelper());
284     const std::unique_ptr<SortedDataInterface> sorted(harnessHelper->newSortedDataInterface(false));
285 
286     {
287         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
288         const std::unique_ptr<SortedDataInterface::Cursor> cursor(sorted->newCursor(opCtx.get()));
289         ASSERT(!cursor->seek(compoundKey1a, true));
290     }
291 
292     {
293         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
294         {
295             WriteUnitOfWork uow(opCtx.get());
296             ASSERT_OK(sorted->insert(opCtx.get(), compoundKey1a, loc1, true));
297             ASSERT_OK(sorted->insert(opCtx.get(), compoundKey1b, loc2, true));
298             ASSERT_OK(sorted->insert(opCtx.get(), compoundKey2b, loc3, true));
299             uow.commit();
300         }
301     }
302 
303     {
304         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
305         const std::unique_ptr<SortedDataInterface::Cursor> cursor(sorted->newCursor(opCtx.get()));
306 
307         ASSERT_EQ(cursor->seek(compoundKey1a, true), IndexKeyEntry(compoundKey1a, loc1));
308         ASSERT_EQ(cursor->next(), IndexKeyEntry(compoundKey1b, loc2));
309         ASSERT_EQ(cursor->next(), IndexKeyEntry(compoundKey2b, loc3));
310         ASSERT_EQ(cursor->next(), boost::none);
311     }
312 
313     {
314         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
315         {
316             WriteUnitOfWork uow(opCtx.get());
317             ASSERT_OK(sorted->insert(opCtx.get(), compoundKey1c, loc4, true));
318             ASSERT_OK(sorted->insert(opCtx.get(), compoundKey3a, loc5, true));
319             uow.commit();
320         }
321     }
322 
323     {
324         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
325         const std::unique_ptr<SortedDataInterface::Cursor> cursor(sorted->newCursor(opCtx.get()));
326 
327         ASSERT_EQ(cursor->seek(compoundKey1a, true), IndexKeyEntry(compoundKey1a, loc1));
328         ASSERT_EQ(cursor->next(), IndexKeyEntry(compoundKey1b, loc2));
329         ASSERT_EQ(cursor->next(), IndexKeyEntry(compoundKey1c, loc4));
330         ASSERT_EQ(cursor->next(), IndexKeyEntry(compoundKey2b, loc3));
331         ASSERT_EQ(cursor->next(), IndexKeyEntry(compoundKey3a, loc5));
332         ASSERT_EQ(cursor->next(), boost::none);
333     }
334 }
335 
336 // Insert multiple compound keys and try to locate them using a reverse cursor
337 // by specifying their exact key and RecordId.
TEST(SortedDataInterface,LocateMultipleCompoundKeysReversed)338 TEST(SortedDataInterface, LocateMultipleCompoundKeysReversed) {
339     const auto harnessHelper(newSortedDataInterfaceHarnessHelper());
340     const std::unique_ptr<SortedDataInterface> sorted(harnessHelper->newSortedDataInterface(false));
341 
342     {
343         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
344         const std::unique_ptr<SortedDataInterface::Cursor> cursor(
345             sorted->newCursor(opCtx.get(), false));
346         ASSERT(!cursor->seek(compoundKey3a, true));
347     }
348 
349     {
350         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
351         {
352             WriteUnitOfWork uow(opCtx.get());
353             ASSERT_OK(sorted->insert(opCtx.get(), compoundKey1a, loc1, true));
354             ASSERT_OK(sorted->insert(opCtx.get(), compoundKey1b, loc2, true));
355             ASSERT_OK(sorted->insert(opCtx.get(), compoundKey2b, loc3, true));
356             uow.commit();
357         }
358     }
359 
360     {
361         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
362         const std::unique_ptr<SortedDataInterface::Cursor> cursor(
363             sorted->newCursor(opCtx.get(), false));
364 
365         ASSERT_EQ(cursor->seek(compoundKey2b, true), IndexKeyEntry(compoundKey2b, loc3));
366         ASSERT_EQ(cursor->next(), IndexKeyEntry(compoundKey1b, loc2));
367         ASSERT_EQ(cursor->next(), IndexKeyEntry(compoundKey1a, loc1));
368         ASSERT_EQ(cursor->next(), boost::none);
369     }
370 
371     {
372         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
373         {
374             WriteUnitOfWork uow(opCtx.get());
375             ASSERT_OK(sorted->insert(opCtx.get(), compoundKey1c, loc4, true));
376             ASSERT_OK(sorted->insert(opCtx.get(), compoundKey3a, loc5, true));
377             uow.commit();
378         }
379     }
380 
381     {
382         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
383         const std::unique_ptr<SortedDataInterface::Cursor> cursor(
384             sorted->newCursor(opCtx.get(), false));
385 
386         ASSERT_EQ(cursor->seek(compoundKey3a, true), IndexKeyEntry(compoundKey3a, loc5));
387         ASSERT_EQ(cursor->next(), IndexKeyEntry(compoundKey2b, loc3));
388         ASSERT_EQ(cursor->next(), IndexKeyEntry(compoundKey1c, loc4));
389         ASSERT_EQ(cursor->next(), IndexKeyEntry(compoundKey1b, loc2));
390         ASSERT_EQ(cursor->next(), IndexKeyEntry(compoundKey1a, loc1));
391         ASSERT_EQ(cursor->next(), boost::none);
392     }
393 }
394 
395 // Insert multiple keys and try to locate them using a forward cursor
396 // by specifying either a smaller key or RecordId.
TEST(SortedDataInterface,LocateIndirect)397 TEST(SortedDataInterface, LocateIndirect) {
398     const auto harnessHelper(newSortedDataInterfaceHarnessHelper());
399     const std::unique_ptr<SortedDataInterface> sorted(harnessHelper->newSortedDataInterface(false));
400 
401     {
402         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
403         const std::unique_ptr<SortedDataInterface::Cursor> cursor(sorted->newCursor(opCtx.get()));
404         ASSERT(!cursor->seek(key1, true));
405     }
406 
407     {
408         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
409         {
410             WriteUnitOfWork uow(opCtx.get());
411             ASSERT_OK(sorted->insert(opCtx.get(), key1, loc1, true));
412             ASSERT_OK(sorted->insert(opCtx.get(), key2, loc2, true));
413             uow.commit();
414         }
415     }
416 
417     {
418         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
419         const std::unique_ptr<SortedDataInterface::Cursor> cursor(sorted->newCursor(opCtx.get()));
420 
421         ASSERT_EQ(cursor->seek(key1, false), IndexKeyEntry(key2, loc2));
422         ASSERT_EQ(cursor->next(), boost::none);
423     }
424 
425     {
426         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
427         {
428             WriteUnitOfWork uow(opCtx.get());
429             ASSERT_OK(sorted->insert(opCtx.get(), key3, loc3, true));
430             uow.commit();
431         }
432     }
433 
434     {
435         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
436         const std::unique_ptr<SortedDataInterface::Cursor> cursor(sorted->newCursor(opCtx.get()));
437 
438         ASSERT_EQ(cursor->seek(key1, true), IndexKeyEntry(key1, loc1));
439         ASSERT_EQ(cursor->next(), IndexKeyEntry(key2, loc2));
440         ASSERT_EQ(cursor->next(), IndexKeyEntry(key3, loc3));
441         ASSERT_EQ(cursor->next(), boost::none);
442     }
443 }
444 
445 // Insert multiple keys and try to locate them using a reverse cursor
446 // by specifying either a larger key or RecordId.
TEST(SortedDataInterface,LocateIndirectReversed)447 TEST(SortedDataInterface, LocateIndirectReversed) {
448     const auto harnessHelper(newSortedDataInterfaceHarnessHelper());
449     const std::unique_ptr<SortedDataInterface> sorted(harnessHelper->newSortedDataInterface(false));
450 
451     {
452         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
453         const std::unique_ptr<SortedDataInterface::Cursor> cursor(
454             sorted->newCursor(opCtx.get(), false));
455         ASSERT(!cursor->seek(key3, true));
456     }
457 
458     {
459         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
460         {
461             WriteUnitOfWork uow(opCtx.get());
462             ASSERT_OK(sorted->insert(opCtx.get(), key1, loc1, true));
463             ASSERT_OK(sorted->insert(opCtx.get(), key2, loc2, true));
464             uow.commit();
465         }
466     }
467 
468     {
469         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
470         const std::unique_ptr<SortedDataInterface::Cursor> cursor(
471             sorted->newCursor(opCtx.get(), false));
472 
473         ASSERT_EQ(cursor->seek(key2, false), IndexKeyEntry(key1, loc1));
474         ASSERT_EQ(cursor->next(), boost::none);
475     }
476 
477     {
478         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
479         {
480             WriteUnitOfWork uow(opCtx.get());
481             ASSERT_OK(sorted->insert(opCtx.get(), key3, loc3, true));
482             uow.commit();
483         }
484     }
485 
486     {
487         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
488         const std::unique_ptr<SortedDataInterface::Cursor> cursor(
489             sorted->newCursor(opCtx.get(), false));
490 
491         ASSERT_EQ(cursor->seek(key3, true), IndexKeyEntry(key3, loc3));
492         ASSERT_EQ(cursor->next(), IndexKeyEntry(key2, loc2));
493         ASSERT_EQ(cursor->next(), IndexKeyEntry(key1, loc1));
494         ASSERT_EQ(cursor->next(), boost::none);
495     }
496 }
497 
498 // Insert multiple compound keys and try to locate them using a forward cursor
499 // by specifying either a smaller key or RecordId.
TEST(SortedDataInterface,LocateIndirectCompoundKeys)500 TEST(SortedDataInterface, LocateIndirectCompoundKeys) {
501     const auto harnessHelper(newSortedDataInterfaceHarnessHelper());
502     const std::unique_ptr<SortedDataInterface> sorted(harnessHelper->newSortedDataInterface(false));
503 
504     {
505         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
506         const std::unique_ptr<SortedDataInterface::Cursor> cursor(sorted->newCursor(opCtx.get()));
507         ASSERT(!cursor->seek(compoundKey1a, true));
508     }
509 
510     {
511         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
512         {
513             WriteUnitOfWork uow(opCtx.get());
514             ASSERT_OK(sorted->insert(opCtx.get(), compoundKey1a, loc1, true));
515             ASSERT_OK(sorted->insert(opCtx.get(), compoundKey1b, loc2, true));
516             ASSERT_OK(sorted->insert(opCtx.get(), compoundKey2b, loc3, true));
517             uow.commit();
518         }
519     }
520 
521     {
522         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
523         const std::unique_ptr<SortedDataInterface::Cursor> cursor(sorted->newCursor(opCtx.get()));
524 
525         ASSERT_EQ(cursor->seek(compoundKey1a, false), IndexKeyEntry(compoundKey1b, loc2));
526         ASSERT_EQ(cursor->next(), IndexKeyEntry(compoundKey2b, loc3));
527         ASSERT_EQ(cursor->next(), boost::none);
528     }
529 
530     {
531         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
532         {
533             WriteUnitOfWork uow(opCtx.get());
534             ASSERT_OK(sorted->insert(opCtx.get(), compoundKey1c, loc4, true));
535             ASSERT_OK(sorted->insert(opCtx.get(), compoundKey3a, loc5, true));
536             uow.commit();
537         }
538     }
539 
540     {
541         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
542         const std::unique_ptr<SortedDataInterface::Cursor> cursor(sorted->newCursor(opCtx.get()));
543 
544         ASSERT_EQ(cursor->seek(compoundKey2a, true), IndexKeyEntry(compoundKey2b, loc3));
545         ASSERT_EQ(cursor->next(), IndexKeyEntry(compoundKey3a, loc5));
546         ASSERT_EQ(cursor->next(), boost::none);
547     }
548 }
549 
550 // Insert multiple compound keys and try to locate them using a reverse cursor
551 // by specifying either a larger key or RecordId.
TEST(SortedDataInterface,LocateIndirectCompoundKeysReversed)552 TEST(SortedDataInterface, LocateIndirectCompoundKeysReversed) {
553     const auto harnessHelper(newSortedDataInterfaceHarnessHelper());
554     const std::unique_ptr<SortedDataInterface> sorted(harnessHelper->newSortedDataInterface(false));
555 
556     {
557         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
558         const std::unique_ptr<SortedDataInterface::Cursor> cursor(
559             sorted->newCursor(opCtx.get(), false));
560         ASSERT(!cursor->seek(compoundKey3a, true));
561     }
562 
563     {
564         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
565         {
566             WriteUnitOfWork uow(opCtx.get());
567             ASSERT_OK(sorted->insert(opCtx.get(), compoundKey1a, loc1, true));
568             ASSERT_OK(sorted->insert(opCtx.get(), compoundKey1b, loc2, true));
569             ASSERT_OK(sorted->insert(opCtx.get(), compoundKey2b, loc3, true));
570             uow.commit();
571         }
572     }
573 
574     {
575         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
576         const std::unique_ptr<SortedDataInterface::Cursor> cursor(
577             sorted->newCursor(opCtx.get(), false));
578 
579         ASSERT_EQ(cursor->seek(compoundKey2b, false), IndexKeyEntry(compoundKey1b, loc2));
580         ASSERT_EQ(cursor->next(), IndexKeyEntry(compoundKey1a, loc1));
581         ASSERT_EQ(cursor->next(), boost::none);
582     }
583 
584     {
585         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
586         {
587             WriteUnitOfWork uow(opCtx.get());
588             ASSERT_OK(sorted->insert(opCtx.get(), compoundKey1c, loc4, true));
589             ASSERT_OK(sorted->insert(opCtx.get(), compoundKey3a, loc5, true));
590             uow.commit();
591         }
592     }
593 
594     {
595         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
596         const std::unique_ptr<SortedDataInterface::Cursor> cursor(
597             sorted->newCursor(opCtx.get(), false));
598 
599         ASSERT_EQ(cursor->seek(compoundKey1d, true), IndexKeyEntry(compoundKey1c, loc4));
600         ASSERT_EQ(cursor->next(), IndexKeyEntry(compoundKey1b, loc2));
601         ASSERT_EQ(cursor->next(), IndexKeyEntry(compoundKey1a, loc1));
602         ASSERT_EQ(cursor->next(), boost::none);
603     }
604 }
605 
606 // Call locate on a forward cursor of an empty index and verify that the cursor
607 // is positioned at EOF.
TEST(SortedDataInterface,LocateEmpty)608 TEST(SortedDataInterface, LocateEmpty) {
609     const auto harnessHelper(newSortedDataInterfaceHarnessHelper());
610     const std::unique_ptr<SortedDataInterface> sorted(harnessHelper->newSortedDataInterface(false));
611 
612     {
613         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
614         ASSERT(sorted->isEmpty(opCtx.get()));
615     }
616 
617     {
618         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
619         const std::unique_ptr<SortedDataInterface::Cursor> cursor(sorted->newCursor(opCtx.get()));
620 
621         ASSERT(!cursor->seek(BSONObj(), true));
622         ASSERT(!cursor->next());
623     }
624 }
625 
626 // Call locate on a reverse cursor of an empty index and verify that the cursor
627 // is positioned at EOF.
TEST(SortedDataInterface,LocateEmptyReversed)628 TEST(SortedDataInterface, LocateEmptyReversed) {
629     const auto harnessHelper(newSortedDataInterfaceHarnessHelper());
630     const std::unique_ptr<SortedDataInterface> sorted(harnessHelper->newSortedDataInterface(false));
631 
632     {
633         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
634         ASSERT(sorted->isEmpty(opCtx.get()));
635     }
636 
637     {
638         const ServiceContext::UniqueOperationContext opCtx(harnessHelper->newOperationContext());
639         const std::unique_ptr<SortedDataInterface::Cursor> cursor(
640             sorted->newCursor(opCtx.get(), false));
641 
642         ASSERT(!cursor->seek(BSONObj(), true));
643         ASSERT(!cursor->next());
644     }
645 }
646 
647 }  // namespace
648 }  // namespace mongo
649