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 }