1 
2 /**
3  *    Copyright (C) 2018-present MongoDB, Inc.
4  *
5  *    This program is free software: you can redistribute it and/or modify
6  *    it under the terms of the Server Side Public License, version 1,
7  *    as published by MongoDB, Inc.
8  *
9  *    This program is distributed in the hope that it will be useful,
10  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *    Server Side Public License for more details.
13  *
14  *    You should have received a copy of the Server Side Public License
15  *    along with this program. If not, see
16  *    <http://www.mongodb.com/licensing/server-side-public-license>.
17  *
18  *    As a special exception, the copyright holders give permission to link the
19  *    code of portions of this program with the OpenSSL library under certain
20  *    conditions as described in each individual source file and distribute
21  *    linked combinations including the program with the OpenSSL library. You
22  *    must comply with the Server Side Public License in all respects for
23  *    all of the code used other than as permitted herein. If you modify file(s)
24  *    with this exception, you may extend this exception to your version of the
25  *    file(s), but you are not obligated to do so. If you do not wish to do so,
26  *    delete this exception statement from your version. If you delete this
27  *    exception statement from all source files in the program, then also delete
28  *    it in the license file.
29  */
30 #include "mongo/platform/basic.h"
31 
32 #include <ostream>
33 
34 #include "mongo/db/jsobj.h"
35 #include "mongo/db/storage/index_entry_comparison.h"
36 
37 namespace mongo {
38 
operator <<(std::ostream & stream,const IndexKeyEntry & entry)39 std::ostream& operator<<(std::ostream& stream, const IndexKeyEntry& entry) {
40     return stream << entry.key << '@' << entry.loc;
41 }
42 
43 // Due to the limitations of various APIs, we need to use the same type (IndexKeyEntry)
44 // for both the stored data and the "query". We cheat and encode extra information in the
45 // first byte of the field names in the query. This works because all stored objects should
46 // have all field names empty, so their first bytes are '\0'.
47 enum BehaviorIfFieldIsEqual {
48     normal = '\0',
49     less = 'l',
50     greater = 'g',
51 };
52 
operator ()(const IndexKeyEntry & lhs,const IndexKeyEntry & rhs) const53 bool IndexEntryComparison::operator()(const IndexKeyEntry& lhs, const IndexKeyEntry& rhs) const {
54     // implementing in memcmp style to ease reuse of this code.
55     return compare(lhs, rhs) < 0;
56 }
57 
58 // This should behave the same as customBSONCmp from btree_logic.cpp.
59 //
60 // Reading the comment in the .h file is highly recommended if you need to understand what this
61 // function is doing
compare(const IndexKeyEntry & lhs,const IndexKeyEntry & rhs) const62 int IndexEntryComparison::compare(const IndexKeyEntry& lhs, const IndexKeyEntry& rhs) const {
63     BSONObjIterator lhsIt(lhs.key);
64     BSONObjIterator rhsIt(rhs.key);
65 
66     // Iterate through both BSONObjects, comparing individual elements one by one
67     for (unsigned mask = 1; lhsIt.more(); mask <<= 1) {
68         if (!rhsIt.more())
69             return _order.descending(mask) ? -1 : 1;
70 
71         const BSONElement l = lhsIt.next();
72         const BSONElement r = rhsIt.next();
73 
74         if (int cmp = l.woCompare(r, /*compareFieldNames=*/false)) {
75             if (cmp == std::numeric_limits<int>::min()) {
76                 // can't be negated
77                 cmp = -1;
78             }
79 
80             return _order.descending(mask) ? -cmp : cmp;
81         }
82 
83         // Here is where the weirdness begins. We sometimes want to fudge the comparison
84         // when a key == the query to implement exclusive ranges.
85         BehaviorIfFieldIsEqual lEqBehavior = BehaviorIfFieldIsEqual(l.fieldName()[0]);
86         BehaviorIfFieldIsEqual rEqBehavior = BehaviorIfFieldIsEqual(r.fieldName()[0]);
87 
88         if (lEqBehavior) {
89             // lhs is the query, rhs is the stored data
90             invariant(rEqBehavior == normal);
91             return lEqBehavior == less ? -1 : 1;
92         }
93 
94         if (rEqBehavior) {
95             // rhs is the query, lhs is the stored data, so reverse the returns
96             invariant(lEqBehavior == normal);
97             return rEqBehavior == less ? 1 : -1;
98         }
99     }
100 
101     if (rhsIt.more())
102         return -1;
103 
104     // This means just look at the key, not the loc.
105     if (lhs.loc.isNull() || rhs.loc.isNull())
106         return 0;
107 
108     return lhs.loc.compare(rhs.loc);  // is supposed to ignore ordering
109 }
110 
111 // reading the comment in the .h file is highly recommended if you need to understand what this
112 // function is doing
makeQueryObject(const BSONObj & keyPrefix,int prefixLen,bool prefixExclusive,const std::vector<const BSONElement * > & keySuffix,const std::vector<bool> & suffixInclusive,const int cursorDirection)113 BSONObj IndexEntryComparison::makeQueryObject(const BSONObj& keyPrefix,
114                                               int prefixLen,
115                                               bool prefixExclusive,
116                                               const std::vector<const BSONElement*>& keySuffix,
117                                               const std::vector<bool>& suffixInclusive,
118                                               const int cursorDirection) {
119     // Please read the comments in the header file to see why this is done.
120     // The basic idea is that we use the field name to store a byte which indicates whether
121     // each field in the query object is inclusive and exclusive, and if it is exclusive, in
122     // which direction.
123     const char exclusiveByte = (cursorDirection == 1 ? greater : less);
124 
125     const StringData exclusiveFieldName(&exclusiveByte, 1);
126 
127     BSONObjBuilder bb;
128 
129     // handle the prefix
130     if (prefixLen > 0) {
131         BSONObjIterator it(keyPrefix);
132         for (int i = 0; i < prefixLen; i++) {
133             invariant(it.more());
134             const BSONElement e = it.next();
135 
136             if (prefixExclusive && i == prefixLen - 1) {
137                 bb.appendAs(e, exclusiveFieldName);
138             } else {
139                 bb.appendAs(e, StringData());
140             }
141         }
142     }
143 
144     // If the prefix is exclusive then the suffix does not matter as it will never be used
145     if (prefixExclusive) {
146         invariant(prefixLen > 0);
147         return bb.obj();
148     }
149 
150     // Handle the suffix. Note that the useful parts of the suffix start at index prefixLen
151     // rather than at 0.
152     invariant(keySuffix.size() == suffixInclusive.size());
153     for (size_t i = prefixLen; i < keySuffix.size(); i++) {
154         invariant(keySuffix[i]);
155         if (suffixInclusive[i]) {
156             bb.appendAs(*keySuffix[i], StringData());
157         } else {
158             bb.appendAs(*keySuffix[i], exclusiveFieldName);
159 
160             // If an exclusive field exists then no fields after this will matter, since an
161             // exclusive field never evaluates as equal
162             return bb.obj();
163         }
164     }
165 
166     return bb.obj();
167 }
168 
169 }  // namespace mongo
170