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