...

Source file src/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_wrappers.go

Documentation: go.mongodb.org/mongo-driver/bson/bsonrw

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

View as plain text