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
31 #include "mongo/bson/util/bson_extract.h"
32
33 #include "mongo/db/jsobj.h"
34 #include "mongo/util/mongoutils/str.h"
35
36 namespace mongo {
37
38 namespace {
39
bsonExtractFieldImpl(const BSONObj & object,StringData fieldName,BSONElement * outElement,bool withDefault)40 Status bsonExtractFieldImpl(const BSONObj& object,
41 StringData fieldName,
42 BSONElement* outElement,
43 bool withDefault) {
44 BSONElement element = object.getField(fieldName);
45
46 if (!element.eoo()) {
47 *outElement = element;
48 return Status::OK();
49 }
50 if (withDefault) {
51 static const Status kDefaultCase(ErrorCodes::NoSuchKey,
52 "bsonExtractFieldImpl default case no such key error");
53 return kDefaultCase;
54 }
55 return Status(ErrorCodes::NoSuchKey,
56 mongoutils::str::stream() << "Missing expected field \"" << fieldName.toString()
57 << "\"");
58 }
59
bsonExtractTypedFieldImpl(const BSONObj & object,StringData fieldName,BSONType type,BSONElement * outElement,bool withDefault)60 Status bsonExtractTypedFieldImpl(const BSONObj& object,
61 StringData fieldName,
62 BSONType type,
63 BSONElement* outElement,
64 bool withDefault) {
65 Status status = bsonExtractFieldImpl(object, fieldName, outElement, withDefault);
66 if (!status.isOK())
67 return status;
68 if (type != outElement->type()) {
69 return Status(ErrorCodes::TypeMismatch,
70 mongoutils::str::stream() << "\"" << fieldName
71 << "\" had the wrong type. Expected "
72 << typeName(type)
73 << ", found "
74 << typeName(outElement->type()));
75 }
76 return status;
77 }
78
bsonExtractIntegerFieldImpl(const BSONObj & object,StringData fieldName,long long * out,bool withDefault)79 Status bsonExtractIntegerFieldImpl(const BSONObj& object,
80 StringData fieldName,
81 long long* out,
82 bool withDefault) {
83 BSONElement element;
84 Status status = bsonExtractFieldImpl(object, fieldName, &element, withDefault);
85 if (!status.isOK())
86 return status;
87 if (!element.isNumber()) {
88 return Status(ErrorCodes::TypeMismatch,
89 mongoutils::str::stream() << "Expected field \"" << fieldName
90 << "\" to have numeric type, but found "
91 << typeName(element.type()));
92 }
93 long long result = element.safeNumberLong();
94 if (result != element.numberDouble()) {
95 return Status(
96 ErrorCodes::BadValue,
97 mongoutils::str::stream() << "Expected field \"" << fieldName
98 << "\" to have a value "
99 "exactly representable as a 64-bit integer, but found "
100 << element);
101 }
102 *out = result;
103 return status;
104 }
105
bsonExtractDoubleFieldImpl(const BSONObj & object,StringData fieldName,double * out,bool withDefault)106 Status bsonExtractDoubleFieldImpl(const BSONObj& object,
107 StringData fieldName,
108 double* out,
109 bool withDefault) {
110 BSONElement element;
111 Status status = bsonExtractField(object, fieldName, &element);
112 if (!status.isOK())
113 return status;
114 if (!element.isNumber()) {
115 return Status(ErrorCodes::TypeMismatch,
116 mongoutils::str::stream() << "Expected field \"" << fieldName
117 << "\" to have numeric type, but found "
118 << typeName(element.type()));
119 }
120 *out = element.numberDouble();
121 return status;
122 }
123 } // namespace
124
125
bsonExtractField(const BSONObj & object,StringData fieldName,BSONElement * outElement)126 Status bsonExtractField(const BSONObj& object, StringData fieldName, BSONElement* outElement) {
127 return bsonExtractFieldImpl(object, fieldName, outElement, false);
128 }
129
bsonExtractTypedField(const BSONObj & object,StringData fieldName,BSONType type,BSONElement * outElement)130 Status bsonExtractTypedField(const BSONObj& object,
131 StringData fieldName,
132 BSONType type,
133 BSONElement* outElement) {
134 return bsonExtractTypedFieldImpl(object, fieldName, type, outElement, false);
135 }
136
bsonExtractBooleanField(const BSONObj & object,StringData fieldName,bool * out)137 Status bsonExtractBooleanField(const BSONObj& object, StringData fieldName, bool* out) {
138 BSONElement element;
139 Status status = bsonExtractTypedField(object, fieldName, Bool, &element);
140 if (status.isOK())
141 *out = element.boolean();
142 return status;
143 }
144
bsonExtractBooleanFieldWithDefault(const BSONObj & object,StringData fieldName,bool defaultValue,bool * out)145 Status bsonExtractBooleanFieldWithDefault(const BSONObj& object,
146 StringData fieldName,
147 bool defaultValue,
148 bool* out) {
149 BSONElement element;
150 Status status = bsonExtractFieldImpl(object, fieldName, &element, true);
151 if (status == ErrorCodes::NoSuchKey) {
152 *out = defaultValue;
153 return Status::OK();
154 }
155
156 if (!status.isOK())
157 return status;
158
159 if (!element.isNumber() && !element.isBoolean()) {
160 return Status(ErrorCodes::TypeMismatch,
161 mongoutils::str::stream() << "Expected boolean or number type for field \""
162 << fieldName
163 << "\", found "
164 << typeName(element.type()));
165 }
166 *out = element.trueValue();
167 return status;
168 }
169
bsonExtractStringField(const BSONObj & object,StringData fieldName,std::string * out)170 Status bsonExtractStringField(const BSONObj& object, StringData fieldName, std::string* out) {
171 BSONElement element;
172 Status status = bsonExtractTypedField(object, fieldName, String, &element);
173 if (status.isOK())
174 *out = element.str();
175 return status;
176 }
177
bsonExtractTimestampField(const BSONObj & object,StringData fieldName,Timestamp * out)178 Status bsonExtractTimestampField(const BSONObj& object, StringData fieldName, Timestamp* out) {
179 BSONElement element;
180 Status status = bsonExtractTypedField(object, fieldName, bsonTimestamp, &element);
181 if (status.isOK())
182 *out = element.timestamp();
183 return status;
184 }
185
bsonExtractOIDField(const BSONObj & object,StringData fieldName,OID * out)186 Status bsonExtractOIDField(const BSONObj& object, StringData fieldName, OID* out) {
187 BSONElement element;
188 Status status = bsonExtractTypedField(object, fieldName, jstOID, &element);
189 if (status.isOK())
190 *out = element.OID();
191 return status;
192 }
193
bsonExtractOIDFieldWithDefault(const BSONObj & object,StringData fieldName,const OID & defaultValue,OID * out)194 Status bsonExtractOIDFieldWithDefault(const BSONObj& object,
195 StringData fieldName,
196 const OID& defaultValue,
197 OID* out) {
198 BSONElement element;
199 Status status = bsonExtractTypedFieldImpl(object, fieldName, jstOID, &element, true);
200 if (status == ErrorCodes::NoSuchKey) {
201 *out = defaultValue;
202 return Status::OK();
203 }
204 if (status.isOK())
205 *out = element.OID();
206 return status;
207 }
208
bsonExtractStringFieldWithDefault(const BSONObj & object,StringData fieldName,StringData defaultValue,std::string * out)209 Status bsonExtractStringFieldWithDefault(const BSONObj& object,
210 StringData fieldName,
211 StringData defaultValue,
212 std::string* out) {
213 BSONElement element;
214 Status status = bsonExtractTypedFieldImpl(object, fieldName, String, &element, true);
215 if (status == ErrorCodes::NoSuchKey) {
216 *out = defaultValue.toString();
217 return Status::OK();
218 }
219 if (status.isOK())
220 *out = element.str();
221 return status;
222 }
223
bsonExtractIntegerField(const BSONObj & object,StringData fieldName,long long * out)224 Status bsonExtractIntegerField(const BSONObj& object, StringData fieldName, long long* out) {
225 return bsonExtractIntegerFieldImpl(object, fieldName, out, false);
226 }
227
bsonExtractDoubleField(const BSONObj & object,StringData fieldName,double * out)228 Status bsonExtractDoubleField(const BSONObj& object, StringData fieldName, double* out) {
229 return bsonExtractDoubleFieldImpl(object, fieldName, out, false);
230 }
231
bsonExtractDoubleFieldWithDefault(const BSONObj & object,StringData fieldName,double defaultValue,double * out)232 Status bsonExtractDoubleFieldWithDefault(const BSONObj& object,
233 StringData fieldName,
234 double defaultValue,
235 double* out) {
236 Status status = bsonExtractDoubleFieldImpl(object, fieldName, out, true);
237 if (status == ErrorCodes::NoSuchKey) {
238 *out = defaultValue;
239 return Status::OK();
240 }
241 return status;
242 }
243
bsonExtractIntegerFieldWithDefault(const BSONObj & object,StringData fieldName,long long defaultValue,long long * out)244 Status bsonExtractIntegerFieldWithDefault(const BSONObj& object,
245 StringData fieldName,
246 long long defaultValue,
247 long long* out) {
248 Status status = bsonExtractIntegerFieldImpl(object, fieldName, out, true);
249 if (status == ErrorCodes::NoSuchKey) {
250 *out = defaultValue;
251 return Status::OK();
252 }
253 return status;
254 }
255
bsonExtractIntegerFieldWithDefaultIf(const BSONObj & object,StringData fieldName,long long defaultValue,stdx::function<bool (long long)> pred,const std::string & predDescription,long long * out)256 Status bsonExtractIntegerFieldWithDefaultIf(const BSONObj& object,
257 StringData fieldName,
258 long long defaultValue,
259 stdx::function<bool(long long)> pred,
260 const std::string& predDescription,
261 long long* out) {
262 Status status = bsonExtractIntegerFieldWithDefault(object, fieldName, defaultValue, out);
263 if (!status.isOK()) {
264 return status;
265 }
266 if (!pred(*out)) {
267 return Status(
268 ErrorCodes::BadValue,
269 mongoutils::str::stream() << "Invalid value in field \"" << fieldName << "\": " << *out
270 << ": "
271 << predDescription);
272 }
273 return status;
274 }
275
bsonExtractIntegerFieldWithDefaultIf(const BSONObj & object,StringData fieldName,long long defaultValue,stdx::function<bool (long long)> pred,long long * out)276 Status bsonExtractIntegerFieldWithDefaultIf(const BSONObj& object,
277 StringData fieldName,
278 long long defaultValue,
279 stdx::function<bool(long long)> pred,
280 long long* out) {
281 return bsonExtractIntegerFieldWithDefaultIf(
282 object, fieldName, defaultValue, pred, "constraint failed", out);
283 }
284
285 } // namespace mongo
286