github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/vendor_skip/go.mongodb.org/mongo-driver/mongo/mongo.go (about)

     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 mongo // import "go.mongodb.org/mongo-driver/mongo"
     8  
     9  import (
    10  	"bytes"
    11  	"context"
    12  	"errors"
    13  	"fmt"
    14  	"io"
    15  	"net"
    16  	"reflect"
    17  	"strconv"
    18  	"strings"
    19  
    20  	"go.mongodb.org/mongo-driver/mongo/options"
    21  	"go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
    22  
    23  	"go.mongodb.org/mongo-driver/bson"
    24  	"go.mongodb.org/mongo-driver/bson/bsoncodec"
    25  	"go.mongodb.org/mongo-driver/bson/bsonrw"
    26  	"go.mongodb.org/mongo-driver/bson/bsontype"
    27  	"go.mongodb.org/mongo-driver/bson/primitive"
    28  )
    29  
    30  // Dialer is used to make network connections.
    31  type Dialer interface {
    32  	DialContext(ctx context.Context, network, address string) (net.Conn, error)
    33  }
    34  
    35  // BSONAppender is an interface implemented by types that can marshal a
    36  // provided type into BSON bytes and append those bytes to the provided []byte.
    37  // The AppendBSON can return a non-nil error and non-nil []byte. The AppendBSON
    38  // method may also write incomplete BSON to the []byte.
    39  //
    40  // Deprecated: BSONAppender is unused and will be removed in Go Driver 2.0.
    41  type BSONAppender interface {
    42  	AppendBSON([]byte, interface{}) ([]byte, error)
    43  }
    44  
    45  // BSONAppenderFunc is an adapter function that allows any function that
    46  // satisfies the AppendBSON method signature to be used where a BSONAppender is
    47  // used.
    48  //
    49  // Deprecated: BSONAppenderFunc is unused and will be removed in Go Driver 2.0.
    50  type BSONAppenderFunc func([]byte, interface{}) ([]byte, error)
    51  
    52  // AppendBSON implements the BSONAppender interface
    53  //
    54  // Deprecated: BSONAppenderFunc is unused and will be removed in Go Driver 2.0.
    55  func (baf BSONAppenderFunc) AppendBSON(dst []byte, val interface{}) ([]byte, error) {
    56  	return baf(dst, val)
    57  }
    58  
    59  // MarshalError is returned when attempting to marshal a value into a document
    60  // results in an error.
    61  type MarshalError struct {
    62  	Value interface{}
    63  	Err   error
    64  }
    65  
    66  // Error implements the error interface.
    67  func (me MarshalError) Error() string {
    68  	return fmt.Sprintf("cannot marshal type %s to a BSON Document: %v", reflect.TypeOf(me.Value), me.Err)
    69  }
    70  
    71  // Pipeline is a type that makes creating aggregation pipelines easier. It is a
    72  // helper and is intended for serializing to BSON.
    73  //
    74  // Example usage:
    75  //
    76  //	mongo.Pipeline{
    77  //		{{"$group", bson.D{{"_id", "$state"}, {"totalPop", bson.D{{"$sum", "$pop"}}}}}},
    78  //		{{"$match", bson.D{{"totalPop", bson.D{{"$gte", 10*1000*1000}}}}}},
    79  //	}
    80  type Pipeline []bson.D
    81  
    82  // bvwPool is a pool of BSON value writers. BSON value writers
    83  var bvwPool = bsonrw.NewBSONValueWriterPool()
    84  
    85  // getEncoder takes a writer, BSON options, and a BSON registry and returns a properly configured
    86  // bson.Encoder that writes to the given writer.
    87  func getEncoder(
    88  	w io.Writer,
    89  	opts *options.BSONOptions,
    90  	reg *bsoncodec.Registry,
    91  ) (*bson.Encoder, error) {
    92  	vw := bvwPool.Get(w)
    93  	enc, err := bson.NewEncoder(vw)
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  
    98  	if opts != nil {
    99  		if opts.ErrorOnInlineDuplicates {
   100  			enc.ErrorOnInlineDuplicates()
   101  		}
   102  		if opts.IntMinSize {
   103  			enc.IntMinSize()
   104  		}
   105  		if opts.NilByteSliceAsEmpty {
   106  			enc.NilByteSliceAsEmpty()
   107  		}
   108  		if opts.NilMapAsEmpty {
   109  			enc.NilMapAsEmpty()
   110  		}
   111  		if opts.NilSliceAsEmpty {
   112  			enc.NilSliceAsEmpty()
   113  		}
   114  		if opts.OmitZeroStruct {
   115  			enc.OmitZeroStruct()
   116  		}
   117  		if opts.StringifyMapKeysWithFmt {
   118  			enc.StringifyMapKeysWithFmt()
   119  		}
   120  		if opts.UseJSONStructTags {
   121  			enc.UseJSONStructTags()
   122  		}
   123  	}
   124  
   125  	if reg != nil {
   126  		// TODO:(GODRIVER-2719): Remove error handling.
   127  		if err := enc.SetRegistry(reg); err != nil {
   128  			return nil, err
   129  		}
   130  	}
   131  
   132  	return enc, nil
   133  }
   134  
   135  // marshal marshals the given value as a BSON document. Byte slices are always converted to a
   136  // bson.Raw before marshaling.
   137  //
   138  // If bsonOpts and registry are specified, the encoder is configured with the requested behaviors.
   139  // If they are nil, the default behaviors are used.
   140  func marshal(
   141  	val interface{},
   142  	bsonOpts *options.BSONOptions,
   143  	registry *bsoncodec.Registry,
   144  ) (bsoncore.Document, error) {
   145  	if registry == nil {
   146  		registry = bson.DefaultRegistry
   147  	}
   148  	if val == nil {
   149  		return nil, ErrNilDocument
   150  	}
   151  	if bs, ok := val.([]byte); ok {
   152  		// Slight optimization so we'll just use MarshalBSON and not go through the codec machinery.
   153  		val = bson.Raw(bs)
   154  	}
   155  
   156  	buf := new(bytes.Buffer)
   157  	enc, err := getEncoder(buf, bsonOpts, registry)
   158  	if err != nil {
   159  		return nil, fmt.Errorf("error configuring BSON encoder: %w", err)
   160  	}
   161  
   162  	err = enc.Encode(val)
   163  	if err != nil {
   164  		return nil, MarshalError{Value: val, Err: err}
   165  	}
   166  
   167  	return buf.Bytes(), nil
   168  }
   169  
   170  // ensureID inserts the given ObjectID as an element named "_id" at the
   171  // beginning of the given BSON document if there is not an "_id" already. If
   172  // there is already an element named "_id", the document is not modified. It
   173  // returns the resulting document and the decoded Go value of the "_id" element.
   174  func ensureID(
   175  	doc bsoncore.Document,
   176  	oid primitive.ObjectID,
   177  	bsonOpts *options.BSONOptions,
   178  	reg *bsoncodec.Registry,
   179  ) (bsoncore.Document, interface{}, error) {
   180  	if reg == nil {
   181  		reg = bson.DefaultRegistry
   182  	}
   183  
   184  	// Try to find the "_id" element. If it exists, try to unmarshal just the
   185  	// "_id" field as an interface{} and return it along with the unmodified
   186  	// BSON document.
   187  	if _, err := doc.LookupErr("_id"); err == nil {
   188  		var id struct {
   189  			ID interface{} `bson:"_id"`
   190  		}
   191  		dec, err := getDecoder(doc, bsonOpts, reg)
   192  		if err != nil {
   193  			return nil, nil, fmt.Errorf("error configuring BSON decoder: %w", err)
   194  		}
   195  		err = dec.Decode(&id)
   196  		if err != nil {
   197  			return nil, nil, fmt.Errorf("error unmarshaling BSON document: %w", err)
   198  		}
   199  
   200  		return doc, id.ID, nil
   201  	}
   202  
   203  	// We couldn't find an "_id" element, so add one with the value of the
   204  	// provided ObjectID.
   205  
   206  	olddoc := doc
   207  
   208  	// Reserve an extra 17 bytes for the "_id" field we're about to add:
   209  	// type (1) + "_id" (3) + terminator (1) + object ID (12)
   210  	const extraSpace = 17
   211  	doc = make(bsoncore.Document, 0, len(olddoc)+extraSpace)
   212  	_, doc = bsoncore.ReserveLength(doc)
   213  	doc = bsoncore.AppendObjectIDElement(doc, "_id", oid)
   214  
   215  	// Remove and re-write the BSON document length header.
   216  	const int32Len = 4
   217  	doc = append(doc, olddoc[int32Len:]...)
   218  	doc = bsoncore.UpdateLength(doc, 0, int32(len(doc)))
   219  
   220  	return doc, oid, nil
   221  }
   222  
   223  func ensureDollarKey(doc bsoncore.Document) error {
   224  	firstElem, err := doc.IndexErr(0)
   225  	if err != nil {
   226  		return errors.New("update document must have at least one element")
   227  	}
   228  
   229  	if !strings.HasPrefix(firstElem.Key(), "$") {
   230  		return errors.New("update document must contain key beginning with '$'")
   231  	}
   232  	return nil
   233  }
   234  
   235  func ensureNoDollarKey(doc bsoncore.Document) error {
   236  	if elem, err := doc.IndexErr(0); err == nil && strings.HasPrefix(elem.Key(), "$") {
   237  		return errors.New("replacement document cannot contain keys beginning with '$'")
   238  	}
   239  
   240  	return nil
   241  }
   242  
   243  func marshalAggregatePipeline(
   244  	pipeline interface{},
   245  	bsonOpts *options.BSONOptions,
   246  	registry *bsoncodec.Registry,
   247  ) (bsoncore.Document, bool, error) {
   248  	switch t := pipeline.(type) {
   249  	case bsoncodec.ValueMarshaler:
   250  		btype, val, err := t.MarshalBSONValue()
   251  		if err != nil {
   252  			return nil, false, err
   253  		}
   254  		if btype != bsontype.Array {
   255  			return nil, false, fmt.Errorf("ValueMarshaler returned a %v, but was expecting %v", btype, bsontype.Array)
   256  		}
   257  
   258  		var hasOutputStage bool
   259  		pipelineDoc := bsoncore.Document(val)
   260  		values, _ := pipelineDoc.Values()
   261  		if pipelineLen := len(values); pipelineLen > 0 {
   262  			if finalDoc, ok := values[pipelineLen-1].DocumentOK(); ok {
   263  				if elem, err := finalDoc.IndexErr(0); err == nil && (elem.Key() == "$out" || elem.Key() == "$merge") {
   264  					hasOutputStage = true
   265  				}
   266  			}
   267  		}
   268  
   269  		return pipelineDoc, hasOutputStage, nil
   270  	default:
   271  		val := reflect.ValueOf(t)
   272  		if !val.IsValid() || (val.Kind() != reflect.Slice && val.Kind() != reflect.Array) {
   273  			return nil, false, fmt.Errorf("can only marshal slices and arrays into aggregation pipelines, but got %v", val.Kind())
   274  		}
   275  
   276  		var hasOutputStage bool
   277  		valLen := val.Len()
   278  
   279  		switch t := pipeline.(type) {
   280  		// Explicitly forbid non-empty pipelines that are semantically single documents
   281  		// and are implemented as slices.
   282  		case bson.D, bson.Raw, bsoncore.Document:
   283  			if valLen > 0 {
   284  				return nil, false,
   285  					fmt.Errorf("%T is not an allowed pipeline type as it represents a single document. Use bson.A or mongo.Pipeline instead", t)
   286  			}
   287  		// bsoncore.Arrays do not need to be marshaled. Only check validity and presence of output stage.
   288  		case bsoncore.Array:
   289  			if err := t.Validate(); err != nil {
   290  				return nil, false, err
   291  			}
   292  
   293  			values, err := t.Values()
   294  			if err != nil {
   295  				return nil, false, err
   296  			}
   297  
   298  			numVals := len(values)
   299  			if numVals == 0 {
   300  				return bsoncore.Document(t), false, nil
   301  			}
   302  
   303  			// If not empty, check if first value of the last stage is $out or $merge.
   304  			if lastStage, ok := values[numVals-1].DocumentOK(); ok {
   305  				if elem, err := lastStage.IndexErr(0); err == nil && (elem.Key() == "$out" || elem.Key() == "$merge") {
   306  					hasOutputStage = true
   307  				}
   308  			}
   309  			return bsoncore.Document(t), hasOutputStage, nil
   310  		}
   311  
   312  		aidx, arr := bsoncore.AppendArrayStart(nil)
   313  		for idx := 0; idx < valLen; idx++ {
   314  			doc, err := marshal(val.Index(idx).Interface(), bsonOpts, registry)
   315  			if err != nil {
   316  				return nil, false, err
   317  			}
   318  
   319  			if idx == valLen-1 {
   320  				if elem, err := doc.IndexErr(0); err == nil && (elem.Key() == "$out" || elem.Key() == "$merge") {
   321  					hasOutputStage = true
   322  				}
   323  			}
   324  			arr = bsoncore.AppendDocumentElement(arr, strconv.Itoa(idx), doc)
   325  		}
   326  		arr, _ = bsoncore.AppendArrayEnd(arr, aidx)
   327  		return arr, hasOutputStage, nil
   328  	}
   329  }
   330  
   331  func marshalUpdateValue(
   332  	update interface{},
   333  	bsonOpts *options.BSONOptions,
   334  	registry *bsoncodec.Registry,
   335  	dollarKeysAllowed bool,
   336  ) (bsoncore.Value, error) {
   337  	documentCheckerFunc := ensureDollarKey
   338  	if !dollarKeysAllowed {
   339  		documentCheckerFunc = ensureNoDollarKey
   340  	}
   341  
   342  	var u bsoncore.Value
   343  	var err error
   344  	switch t := update.(type) {
   345  	case nil:
   346  		return u, ErrNilDocument
   347  	case primitive.D:
   348  		u.Type = bsontype.EmbeddedDocument
   349  		u.Data, err = marshal(update, bsonOpts, registry)
   350  		if err != nil {
   351  			return u, err
   352  		}
   353  
   354  		return u, documentCheckerFunc(u.Data)
   355  	case bson.Raw:
   356  		u.Type = bsontype.EmbeddedDocument
   357  		u.Data = t
   358  		return u, documentCheckerFunc(u.Data)
   359  	case bsoncore.Document:
   360  		u.Type = bsontype.EmbeddedDocument
   361  		u.Data = t
   362  		return u, documentCheckerFunc(u.Data)
   363  	case []byte:
   364  		u.Type = bsontype.EmbeddedDocument
   365  		u.Data = t
   366  		return u, documentCheckerFunc(u.Data)
   367  	case bsoncodec.Marshaler:
   368  		u.Type = bsontype.EmbeddedDocument
   369  		u.Data, err = t.MarshalBSON()
   370  		if err != nil {
   371  			return u, err
   372  		}
   373  
   374  		return u, documentCheckerFunc(u.Data)
   375  	case bsoncodec.ValueMarshaler:
   376  		u.Type, u.Data, err = t.MarshalBSONValue()
   377  		if err != nil {
   378  			return u, err
   379  		}
   380  		if u.Type != bsontype.Array && u.Type != bsontype.EmbeddedDocument {
   381  			return u, fmt.Errorf("ValueMarshaler returned a %v, but was expecting %v or %v", u.Type, bsontype.Array, bsontype.EmbeddedDocument)
   382  		}
   383  		return u, err
   384  	default:
   385  		val := reflect.ValueOf(t)
   386  		if !val.IsValid() {
   387  			return u, fmt.Errorf("can only marshal slices and arrays into update pipelines, but got %v", val.Kind())
   388  		}
   389  		if val.Kind() != reflect.Slice && val.Kind() != reflect.Array {
   390  			u.Type = bsontype.EmbeddedDocument
   391  			u.Data, err = marshal(update, bsonOpts, registry)
   392  			if err != nil {
   393  				return u, err
   394  			}
   395  
   396  			return u, documentCheckerFunc(u.Data)
   397  		}
   398  
   399  		u.Type = bsontype.Array
   400  		aidx, arr := bsoncore.AppendArrayStart(nil)
   401  		valLen := val.Len()
   402  		for idx := 0; idx < valLen; idx++ {
   403  			doc, err := marshal(val.Index(idx).Interface(), bsonOpts, registry)
   404  			if err != nil {
   405  				return u, err
   406  			}
   407  
   408  			if err := documentCheckerFunc(doc); err != nil {
   409  				return u, err
   410  			}
   411  
   412  			arr = bsoncore.AppendDocumentElement(arr, strconv.Itoa(idx), doc)
   413  		}
   414  		u.Data, _ = bsoncore.AppendArrayEnd(arr, aidx)
   415  		return u, err
   416  	}
   417  }
   418  
   419  func marshalValue(
   420  	val interface{},
   421  	bsonOpts *options.BSONOptions,
   422  	registry *bsoncodec.Registry,
   423  ) (bsoncore.Value, error) {
   424  	if registry == nil {
   425  		registry = bson.DefaultRegistry
   426  	}
   427  	if val == nil {
   428  		return bsoncore.Value{}, ErrNilValue
   429  	}
   430  
   431  	buf := new(bytes.Buffer)
   432  	enc, err := getEncoder(buf, bsonOpts, registry)
   433  	if err != nil {
   434  		return bsoncore.Value{}, fmt.Errorf("error configuring BSON encoder: %w", err)
   435  	}
   436  
   437  	// Encode the value in a single-element document with an empty key. Use bsoncore to extract the
   438  	// first element and return the BSON value.
   439  	err = enc.Encode(bson.D{{Key: "", Value: val}})
   440  	if err != nil {
   441  		return bsoncore.Value{}, MarshalError{Value: val, Err: err}
   442  	}
   443  	return bsoncore.Document(buf.Bytes()).Index(0).Value(), nil
   444  }
   445  
   446  // Build the aggregation pipeline for the CountDocument command.
   447  func countDocumentsAggregatePipeline(
   448  	filter interface{},
   449  	encOpts *options.BSONOptions,
   450  	registry *bsoncodec.Registry,
   451  	opts *options.CountOptions,
   452  ) (bsoncore.Document, error) {
   453  	filterDoc, err := marshal(filter, encOpts, registry)
   454  	if err != nil {
   455  		return nil, err
   456  	}
   457  
   458  	aidx, arr := bsoncore.AppendArrayStart(nil)
   459  	didx, arr := bsoncore.AppendDocumentElementStart(arr, strconv.Itoa(0))
   460  	arr = bsoncore.AppendDocumentElement(arr, "$match", filterDoc)
   461  	arr, _ = bsoncore.AppendDocumentEnd(arr, didx)
   462  
   463  	index := 1
   464  	if opts != nil {
   465  		if opts.Skip != nil {
   466  			didx, arr = bsoncore.AppendDocumentElementStart(arr, strconv.Itoa(index))
   467  			arr = bsoncore.AppendInt64Element(arr, "$skip", *opts.Skip)
   468  			arr, _ = bsoncore.AppendDocumentEnd(arr, didx)
   469  			index++
   470  		}
   471  		if opts.Limit != nil {
   472  			didx, arr = bsoncore.AppendDocumentElementStart(arr, strconv.Itoa(index))
   473  			arr = bsoncore.AppendInt64Element(arr, "$limit", *opts.Limit)
   474  			arr, _ = bsoncore.AppendDocumentEnd(arr, didx)
   475  			index++
   476  		}
   477  	}
   478  
   479  	didx, arr = bsoncore.AppendDocumentElementStart(arr, strconv.Itoa(index))
   480  	iidx, arr := bsoncore.AppendDocumentElementStart(arr, "$group")
   481  	arr = bsoncore.AppendInt32Element(arr, "_id", 1)
   482  	iiidx, arr := bsoncore.AppendDocumentElementStart(arr, "n")
   483  	arr = bsoncore.AppendInt32Element(arr, "$sum", 1)
   484  	arr, _ = bsoncore.AppendDocumentEnd(arr, iiidx)
   485  	arr, _ = bsoncore.AppendDocumentEnd(arr, iidx)
   486  	arr, _ = bsoncore.AppendDocumentEnd(arr, didx)
   487  
   488  	return bsoncore.AppendArrayEnd(arr, aidx)
   489  }