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