blob: 196662ca0313e86d5bfb4ce32bac536ce54d56b3 [file] [log] [blame]
Don Newton379ae252019-04-01 12:17:06 -04001// Copyright (C) MongoDB, Inc. 2017-present.
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may
4// not use this file except in compliance with the License. You may obtain
5// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6
7package bsonrw
8
9import (
10 "encoding/base64"
11 "errors"
12 "fmt"
13 "math"
14 "strconv"
15 "time"
16
17 "github.com/mongodb/mongo-go-driver/bson/bsontype"
18 "github.com/mongodb/mongo-go-driver/bson/primitive"
19)
20
21func wrapperKeyBSONType(key string) bsontype.Type {
22 switch string(key) {
23 case "$numberInt":
24 return bsontype.Int32
25 case "$numberLong":
26 return bsontype.Int64
27 case "$oid":
28 return bsontype.ObjectID
29 case "$symbol":
30 return bsontype.Symbol
31 case "$numberDouble":
32 return bsontype.Double
33 case "$numberDecimal":
34 return bsontype.Decimal128
35 case "$binary":
36 return bsontype.Binary
37 case "$code":
38 return bsontype.JavaScript
39 case "$scope":
40 return bsontype.CodeWithScope
41 case "$timestamp":
42 return bsontype.Timestamp
43 case "$regularExpression":
44 return bsontype.Regex
45 case "$dbPointer":
46 return bsontype.DBPointer
47 case "$date":
48 return bsontype.DateTime
49 case "$ref":
50 fallthrough
51 case "$id":
52 fallthrough
53 case "$db":
54 return bsontype.EmbeddedDocument // dbrefs aren't bson types
55 case "$minKey":
56 return bsontype.MinKey
57 case "$maxKey":
58 return bsontype.MaxKey
59 case "$undefined":
60 return bsontype.Undefined
61 }
62
63 return bsontype.EmbeddedDocument
64}
65
66func (ejv *extJSONValue) parseBinary() (b []byte, subType byte, err error) {
67 if ejv.t != bsontype.EmbeddedDocument {
68 return nil, 0, fmt.Errorf("$binary value should be object, but instead is %s", ejv.t)
69 }
70
71 binObj := ejv.v.(*extJSONObject)
72 bFound := false
73 stFound := false
74
75 for i, key := range binObj.keys {
76 val := binObj.values[i]
77
78 switch key {
79 case "base64":
80 if bFound {
81 return nil, 0, errors.New("duplicate base64 key in $binary")
82 }
83
84 if val.t != bsontype.String {
85 return nil, 0, fmt.Errorf("$binary base64 value should be string, but instead is %s", val.t)
86 }
87
88 base64Bytes, err := base64.StdEncoding.DecodeString(val.v.(string))
89 if err != nil {
90 return nil, 0, fmt.Errorf("invalid $binary base64 string: %s", val.v.(string))
91 }
92
93 b = base64Bytes
94 bFound = true
95 case "subType":
96 if stFound {
97 return nil, 0, errors.New("duplicate subType key in $binary")
98 }
99
100 if val.t != bsontype.String {
101 return nil, 0, fmt.Errorf("$binary subType value should be string, but instead is %s", val.t)
102 }
103
104 i, err := strconv.ParseInt(val.v.(string), 16, 64)
105 if err != nil {
106 return nil, 0, fmt.Errorf("invalid $binary subType string: %s", val.v.(string))
107 }
108
109 subType = byte(i)
110 stFound = true
111 default:
112 return nil, 0, fmt.Errorf("invalid key in $binary object: %s", key)
113 }
114 }
115
116 if !bFound {
117 return nil, 0, errors.New("missing base64 field in $binary object")
118 }
119
120 if !stFound {
121 return nil, 0, errors.New("missing subType field in $binary object")
122
123 }
124
125 return b, subType, nil
126}
127
128func (ejv *extJSONValue) parseDBPointer() (ns string, oid primitive.ObjectID, err error) {
129 if ejv.t != bsontype.EmbeddedDocument {
130 return "", primitive.NilObjectID, fmt.Errorf("$dbPointer value should be object, but instead is %s", ejv.t)
131 }
132
133 dbpObj := ejv.v.(*extJSONObject)
134 oidFound := false
135 nsFound := false
136
137 for i, key := range dbpObj.keys {
138 val := dbpObj.values[i]
139
140 switch key {
141 case "$ref":
142 if nsFound {
143 return "", primitive.NilObjectID, errors.New("duplicate $ref key in $dbPointer")
144 }
145
146 if val.t != bsontype.String {
147 return "", primitive.NilObjectID, fmt.Errorf("$dbPointer $ref value should be string, but instead is %s", val.t)
148 }
149
150 ns = val.v.(string)
151 nsFound = true
152 case "$id":
153 if oidFound {
154 return "", primitive.NilObjectID, errors.New("duplicate $id key in $dbPointer")
155 }
156
157 if val.t != bsontype.String {
158 return "", primitive.NilObjectID, fmt.Errorf("$dbPointer $id value should be string, but instead is %s", val.t)
159 }
160
161 oid, err = primitive.ObjectIDFromHex(val.v.(string))
162 if err != nil {
163 return "", primitive.NilObjectID, err
164 }
165
166 oidFound = true
167 default:
168 return "", primitive.NilObjectID, fmt.Errorf("invalid key in $dbPointer object: %s", key)
169 }
170 }
171
172 if !nsFound {
173 return "", oid, errors.New("missing $ref field in $dbPointer object")
174 }
175
176 if !oidFound {
177 return "", oid, errors.New("missing $id field in $dbPointer object")
178 }
179
180 return ns, oid, nil
181}
182
183const rfc3339Milli = "2006-01-02T15:04:05.999Z07:00"
184
185func (ejv *extJSONValue) parseDateTime() (int64, error) {
186 switch ejv.t {
187 case bsontype.Int32:
188 return int64(ejv.v.(int32)), nil
189 case bsontype.Int64:
190 return ejv.v.(int64), nil
191 case bsontype.String:
192 return parseDatetimeString(ejv.v.(string))
193 case bsontype.EmbeddedDocument:
194 return parseDatetimeObject(ejv.v.(*extJSONObject))
195 default:
196 return 0, fmt.Errorf("$date value should be string or object, but instead is %s", ejv.t)
197 }
198}
199
200func parseDatetimeString(data string) (int64, error) {
201 t, err := time.Parse(rfc3339Milli, data)
202 if err != nil {
203 return 0, fmt.Errorf("invalid $date value string: %s", data)
204 }
205
206 return t.UnixNano() / 1e6, nil
207}
208
209func parseDatetimeObject(data *extJSONObject) (d int64, err error) {
210 dFound := false
211
212 for i, key := range data.keys {
213 val := data.values[i]
214
215 switch key {
216 case "$numberLong":
217 if dFound {
218 return 0, errors.New("duplicate $numberLong key in $date")
219 }
220
221 if val.t != bsontype.String {
222 return 0, fmt.Errorf("$date $numberLong field should be string, but instead is %s", val.t)
223 }
224
225 d, err = val.parseInt64()
226 if err != nil {
227 return 0, err
228 }
229 dFound = true
230 default:
231 return 0, fmt.Errorf("invalid key in $date object: %s", key)
232 }
233 }
234
235 if !dFound {
236 return 0, errors.New("missing $numberLong field in $date object")
237 }
238
239 return d, nil
240}
241
242func (ejv *extJSONValue) parseDecimal128() (primitive.Decimal128, error) {
243 if ejv.t != bsontype.String {
244 return primitive.Decimal128{}, fmt.Errorf("$numberDecimal value should be string, but instead is %s", ejv.t)
245 }
246
247 d, err := primitive.ParseDecimal128(ejv.v.(string))
248 if err != nil {
249 return primitive.Decimal128{}, fmt.Errorf("$invalid $numberDecimal string: %s", ejv.v.(string))
250 }
251
252 return d, nil
253}
254
255func (ejv *extJSONValue) parseDouble() (float64, error) {
256 if ejv.t == bsontype.Double {
257 return ejv.v.(float64), nil
258 }
259
260 if ejv.t != bsontype.String {
261 return 0, fmt.Errorf("$numberDouble value should be string, but instead is %s", ejv.t)
262 }
263
264 switch string(ejv.v.(string)) {
265 case "Infinity":
266 return math.Inf(1), nil
267 case "-Infinity":
268 return math.Inf(-1), nil
269 case "NaN":
270 return math.NaN(), nil
271 }
272
273 f, err := strconv.ParseFloat(ejv.v.(string), 64)
274 if err != nil {
275 return 0, err
276 }
277
278 return f, nil
279}
280
281func (ejv *extJSONValue) parseInt32() (int32, error) {
282 if ejv.t == bsontype.Int32 {
283 return ejv.v.(int32), nil
284 }
285
286 if ejv.t != bsontype.String {
287 return 0, fmt.Errorf("$numberInt value should be string, but instead is %s", ejv.t)
288 }
289
290 i, err := strconv.ParseInt(ejv.v.(string), 10, 64)
291 if err != nil {
292 return 0, err
293 }
294
295 if i < math.MinInt32 || i > math.MaxInt32 {
296 return 0, fmt.Errorf("$numberInt value should be int32 but instead is int64: %d", i)
297 }
298
299 return int32(i), nil
300}
301
302func (ejv *extJSONValue) parseInt64() (int64, error) {
303 if ejv.t == bsontype.Int64 {
304 return ejv.v.(int64), nil
305 }
306
307 if ejv.t != bsontype.String {
308 return 0, fmt.Errorf("$numberLong value should be string, but instead is %s", ejv.t)
309 }
310
311 i, err := strconv.ParseInt(ejv.v.(string), 10, 64)
312 if err != nil {
313 return 0, err
314 }
315
316 return i, nil
317}
318
319func (ejv *extJSONValue) parseJavascript() (code string, err error) {
320 if ejv.t != bsontype.String {
321 return "", fmt.Errorf("$code value should be string, but instead is %s", ejv.t)
322 }
323
324 return ejv.v.(string), nil
325}
326
327func (ejv *extJSONValue) parseMinMaxKey(minmax string) error {
328 if ejv.t != bsontype.Int32 {
329 return fmt.Errorf("$%sKey value should be int32, but instead is %s", minmax, ejv.t)
330 }
331
332 if ejv.v.(int32) != 1 {
333 return fmt.Errorf("$%sKey value must be 1, but instead is %d", minmax, ejv.v.(int32))
334 }
335
336 return nil
337}
338
339func (ejv *extJSONValue) parseObjectID() (primitive.ObjectID, error) {
340 if ejv.t != bsontype.String {
341 return primitive.NilObjectID, fmt.Errorf("$oid value should be string, but instead is %s", ejv.t)
342 }
343
344 return primitive.ObjectIDFromHex(ejv.v.(string))
345}
346
347func (ejv *extJSONValue) parseRegex() (pattern, options string, err error) {
348 if ejv.t != bsontype.EmbeddedDocument {
349 return "", "", fmt.Errorf("$regularExpression value should be object, but instead is %s", ejv.t)
350 }
351
352 regexObj := ejv.v.(*extJSONObject)
353 patFound := false
354 optFound := false
355
356 for i, key := range regexObj.keys {
357 val := regexObj.values[i]
358
359 switch string(key) {
360 case "pattern":
361 if patFound {
362 return "", "", errors.New("duplicate pattern key in $regularExpression")
363 }
364
365 if val.t != bsontype.String {
366 return "", "", fmt.Errorf("$regularExpression pattern value should be string, but instead is %s", val.t)
367 }
368
369 pattern = val.v.(string)
370 patFound = true
371 case "options":
372 if optFound {
373 return "", "", errors.New("duplicate options key in $regularExpression")
374 }
375
376 if val.t != bsontype.String {
377 return "", "", fmt.Errorf("$regularExpression options value should be string, but instead is %s", val.t)
378 }
379
380 options = val.v.(string)
381 optFound = true
382 default:
383 return "", "", fmt.Errorf("invalid key in $regularExpression object: %s", key)
384 }
385 }
386
387 if !patFound {
388 return "", "", errors.New("missing pattern field in $regularExpression object")
389 }
390
391 if !optFound {
392 return "", "", errors.New("missing options field in $regularExpression object")
393
394 }
395
396 return pattern, options, nil
397}
398
399func (ejv *extJSONValue) parseSymbol() (string, error) {
400 if ejv.t != bsontype.String {
401 return "", fmt.Errorf("$symbol value should be string, but instead is %s", ejv.t)
402 }
403
404 return ejv.v.(string), nil
405}
406
407func (ejv *extJSONValue) parseTimestamp() (t, i uint32, err error) {
408 if ejv.t != bsontype.EmbeddedDocument {
409 return 0, 0, fmt.Errorf("$timestamp value should be object, but instead is %s", ejv.t)
410 }
411
412 handleKey := func(key string, val *extJSONValue, flag bool) (uint32, error) {
413 if flag {
414 return 0, fmt.Errorf("duplicate %s key in $timestamp", key)
415 }
416
417 switch val.t {
418 case bsontype.Int32:
419 if val.v.(int32) < 0 {
420 return 0, fmt.Errorf("$timestamp %s number should be uint32: %s", key, string(val.v.(int32)))
421 }
422
423 return uint32(val.v.(int32)), nil
424 case bsontype.Int64:
425 if val.v.(int64) < 0 || uint32(val.v.(int64)) > math.MaxUint32 {
426 return 0, fmt.Errorf("$timestamp %s number should be uint32: %s", key, string(val.v.(int32)))
427 }
428
429 return uint32(val.v.(int64)), nil
430 default:
431 return 0, fmt.Errorf("$timestamp %s value should be uint32, but instead is %s", key, val.t)
432 }
433 }
434
435 tsObj := ejv.v.(*extJSONObject)
436 tFound := false
437 iFound := false
438
439 for j, key := range tsObj.keys {
440 val := tsObj.values[j]
441
442 switch key {
443 case "t":
444 if t, err = handleKey(key, val, tFound); err != nil {
445 return 0, 0, err
446 }
447
448 tFound = true
449 case "i":
450 if i, err = handleKey(key, val, iFound); err != nil {
451 return 0, 0, err
452 }
453
454 iFound = true
455 default:
456 return 0, 0, fmt.Errorf("invalid key in $timestamp object: %s", key)
457 }
458 }
459
460 if !tFound {
461 return 0, 0, errors.New("missing t field in $timestamp object")
462 }
463
464 if !iFound {
465 return 0, 0, errors.New("missing i field in $timestamp object")
466 }
467
468 return t, i, nil
469}
470
471func (ejv *extJSONValue) parseUndefined() error {
472 if ejv.t != bsontype.Boolean {
473 return fmt.Errorf("undefined value should be boolean, but instead is %s", ejv.t)
474 }
475
476 if !ejv.v.(bool) {
477 return fmt.Errorf("$undefined balue boolean should be true, but instead is %v", ejv.v.(bool))
478 }
479
480 return nil
481}