github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/vendor_skip/go.mongodb.org/mongo-driver/mongo/database.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 8 9 import ( 10 "context" 11 "errors" 12 "fmt" 13 "time" 14 15 "go.mongodb.org/mongo-driver/bson" 16 "go.mongodb.org/mongo-driver/bson/bsoncodec" 17 "go.mongodb.org/mongo-driver/internal" 18 "go.mongodb.org/mongo-driver/mongo/description" 19 "go.mongodb.org/mongo-driver/mongo/options" 20 "go.mongodb.org/mongo-driver/mongo/readconcern" 21 "go.mongodb.org/mongo-driver/mongo/readpref" 22 "go.mongodb.org/mongo-driver/mongo/writeconcern" 23 "go.mongodb.org/mongo-driver/x/bsonx/bsoncore" 24 "go.mongodb.org/mongo-driver/x/mongo/driver" 25 "go.mongodb.org/mongo-driver/x/mongo/driver/operation" 26 "go.mongodb.org/mongo-driver/x/mongo/driver/session" 27 ) 28 29 var ( 30 defaultRunCmdOpts = []*options.RunCmdOptions{options.RunCmd().SetReadPreference(readpref.Primary())} 31 ) 32 33 // Database is a handle to a MongoDB database. It is safe for concurrent use by multiple goroutines. 34 type Database struct { 35 client *Client 36 name string 37 readConcern *readconcern.ReadConcern 38 writeConcern *writeconcern.WriteConcern 39 readPreference *readpref.ReadPref 40 readSelector description.ServerSelector 41 writeSelector description.ServerSelector 42 bsonOpts *options.BSONOptions 43 registry *bsoncodec.Registry 44 } 45 46 func newDatabase(client *Client, name string, opts ...*options.DatabaseOptions) *Database { 47 dbOpt := options.MergeDatabaseOptions(opts...) 48 49 rc := client.readConcern 50 if dbOpt.ReadConcern != nil { 51 rc = dbOpt.ReadConcern 52 } 53 54 rp := client.readPreference 55 if dbOpt.ReadPreference != nil { 56 rp = dbOpt.ReadPreference 57 } 58 59 wc := client.writeConcern 60 if dbOpt.WriteConcern != nil { 61 wc = dbOpt.WriteConcern 62 } 63 64 bsonOpts := client.bsonOpts 65 if dbOpt.BSONOptions != nil { 66 bsonOpts = dbOpt.BSONOptions 67 } 68 69 reg := client.registry 70 if dbOpt.Registry != nil { 71 reg = dbOpt.Registry 72 } 73 74 db := &Database{ 75 client: client, 76 name: name, 77 readPreference: rp, 78 readConcern: rc, 79 writeConcern: wc, 80 bsonOpts: bsonOpts, 81 registry: reg, 82 } 83 84 db.readSelector = description.CompositeSelector([]description.ServerSelector{ 85 description.ReadPrefSelector(db.readPreference), 86 description.LatencySelector(db.client.localThreshold), 87 }) 88 89 db.writeSelector = description.CompositeSelector([]description.ServerSelector{ 90 description.WriteSelector(), 91 description.LatencySelector(db.client.localThreshold), 92 }) 93 94 return db 95 } 96 97 // Client returns the Client the Database was created from. 98 func (db *Database) Client() *Client { 99 return db.client 100 } 101 102 // Name returns the name of the database. 103 func (db *Database) Name() string { 104 return db.name 105 } 106 107 // Collection gets a handle for a collection with the given name configured with the given CollectionOptions. 108 func (db *Database) Collection(name string, opts ...*options.CollectionOptions) *Collection { 109 return newCollection(db, name, opts...) 110 } 111 112 // Aggregate executes an aggregate command the database. This requires MongoDB version >= 3.6 and driver version >= 113 // 1.1.0. 114 // 115 // The pipeline parameter must be a slice of documents, each representing an aggregation stage. The pipeline 116 // cannot be nil but can be empty. The stage documents must all be non-nil. For a pipeline of bson.D documents, the 117 // mongo.Pipeline type can be used. See 118 // https://www.mongodb.com/docs/manual/reference/operator/aggregation-pipeline/#db-aggregate-stages for a list of valid 119 // stages in database-level aggregations. 120 // 121 // The opts parameter can be used to specify options for this operation (see the options.AggregateOptions documentation). 122 // 123 // For more information about the command, see https://www.mongodb.com/docs/manual/reference/command/aggregate/. 124 func (db *Database) Aggregate(ctx context.Context, pipeline interface{}, 125 opts ...*options.AggregateOptions) (*Cursor, error) { 126 a := aggregateParams{ 127 ctx: ctx, 128 pipeline: pipeline, 129 client: db.client, 130 registry: db.registry, 131 readConcern: db.readConcern, 132 writeConcern: db.writeConcern, 133 retryRead: db.client.retryReads, 134 db: db.name, 135 readSelector: db.readSelector, 136 writeSelector: db.writeSelector, 137 readPreference: db.readPreference, 138 opts: opts, 139 } 140 return aggregate(a) 141 } 142 143 func (db *Database) processRunCommand(ctx context.Context, cmd interface{}, 144 cursorCommand bool, opts ...*options.RunCmdOptions) (*operation.Command, *session.Client, error) { 145 sess := sessionFromContext(ctx) 146 if sess == nil && db.client.sessionPool != nil { 147 sess = session.NewImplicitClientSession(db.client.sessionPool, db.client.id) 148 } 149 150 err := db.client.validSession(sess) 151 if err != nil { 152 return nil, sess, err 153 } 154 155 ro := options.MergeRunCmdOptions(append(defaultRunCmdOpts, opts...)...) 156 if sess != nil && sess.TransactionRunning() && ro.ReadPreference != nil && ro.ReadPreference.Mode() != readpref.PrimaryMode { 157 return nil, sess, errors.New("read preference in a transaction must be primary") 158 } 159 160 if isUnorderedMap(cmd) { 161 return nil, sess, ErrMapForOrderedArgument{"cmd"} 162 } 163 164 runCmdDoc, err := marshal(cmd, db.bsonOpts, db.registry) 165 if err != nil { 166 return nil, sess, err 167 } 168 readSelect := description.CompositeSelector([]description.ServerSelector{ 169 description.ReadPrefSelector(ro.ReadPreference), 170 description.LatencySelector(db.client.localThreshold), 171 }) 172 if sess != nil && sess.PinnedServer != nil { 173 readSelect = makePinnedSelector(sess, readSelect) 174 } 175 176 var op *operation.Command 177 switch cursorCommand { 178 case true: 179 cursorOpts := db.client.createBaseCursorOptions() 180 op = operation.NewCursorCommand(runCmdDoc, cursorOpts) 181 default: 182 op = operation.NewCommand(runCmdDoc) 183 } 184 185 // TODO(GODRIVER-2649): ReadConcern(db.readConcern) will not actually pass the database's 186 // read concern. Remove this note once readConcern is correctly passed to the operation 187 // level. 188 return op.Session(sess).CommandMonitor(db.client.monitor). 189 ServerSelector(readSelect).ClusterClock(db.client.clock). 190 Database(db.name).Deployment(db.client.deployment).ReadConcern(db.readConcern). 191 Crypt(db.client.cryptFLE).ReadPreference(ro.ReadPreference).ServerAPI(db.client.serverAPI). 192 Timeout(db.client.timeout).Logger(db.client.logger), sess, nil 193 } 194 195 // RunCommand executes the given command against the database. This function does not obey the Database's read 196 // preference. To specify a read preference, the RunCmdOptions.ReadPreference option must be used. 197 // 198 // The runCommand parameter must be a document for the command to be executed. It cannot be nil. 199 // This must be an order-preserving type such as bson.D. Map types such as bson.M are not valid. 200 // 201 // The opts parameter can be used to specify options for this operation (see the options.RunCmdOptions documentation). 202 // 203 // The behavior of RunCommand is undefined if the command document contains any of the following: 204 // - A session ID or any transaction-specific fields 205 // - API versioning options when an API version is already declared on the Client 206 // - maxTimeMS when Timeout is set on the Client 207 func (db *Database) RunCommand(ctx context.Context, runCommand interface{}, opts ...*options.RunCmdOptions) *SingleResult { 208 if ctx == nil { 209 ctx = context.Background() 210 } 211 212 op, sess, err := db.processRunCommand(ctx, runCommand, false, opts...) 213 defer closeImplicitSession(sess) 214 if err != nil { 215 return &SingleResult{err: err} 216 } 217 218 err = op.Execute(ctx) 219 // RunCommand can be used to run a write, thus execute may return a write error 220 _, convErr := processWriteError(err) 221 return &SingleResult{ 222 ctx: ctx, 223 err: convErr, 224 rdr: bson.Raw(op.Result()), 225 bsonOpts: db.bsonOpts, 226 reg: db.registry, 227 } 228 } 229 230 // RunCommandCursor executes the given command against the database and parses the response as a cursor. If the command 231 // being executed does not return a cursor (e.g. insert), the command will be executed on the server and an error will 232 // be returned because the server response cannot be parsed as a cursor. This function does not obey the Database's read 233 // preference. To specify a read preference, the RunCmdOptions.ReadPreference option must be used. 234 // 235 // The runCommand parameter must be a document for the command to be executed. It cannot be nil. 236 // This must be an order-preserving type such as bson.D. Map types such as bson.M are not valid. 237 // 238 // The opts parameter can be used to specify options for this operation (see the options.RunCmdOptions documentation). 239 // 240 // The behavior of RunCommandCursor is undefined if the command document contains any of the following: 241 // - A session ID or any transaction-specific fields 242 // - API versioning options when an API version is already declared on the Client 243 // - maxTimeMS when Timeout is set on the Client 244 func (db *Database) RunCommandCursor(ctx context.Context, runCommand interface{}, opts ...*options.RunCmdOptions) (*Cursor, error) { 245 if ctx == nil { 246 ctx = context.Background() 247 } 248 249 op, sess, err := db.processRunCommand(ctx, runCommand, true, opts...) 250 if err != nil { 251 closeImplicitSession(sess) 252 return nil, replaceErrors(err) 253 } 254 255 if err = op.Execute(ctx); err != nil { 256 closeImplicitSession(sess) 257 return nil, replaceErrors(err) 258 } 259 260 bc, err := op.ResultCursor() 261 if err != nil { 262 closeImplicitSession(sess) 263 return nil, replaceErrors(err) 264 } 265 cursor, err := newCursorWithSession(bc, db.bsonOpts, db.registry, sess) 266 return cursor, replaceErrors(err) 267 } 268 269 // Drop drops the database on the server. This method ignores "namespace not found" errors so it is safe to drop 270 // a database that does not exist on the server. 271 func (db *Database) Drop(ctx context.Context) error { 272 if ctx == nil { 273 ctx = context.Background() 274 } 275 276 sess := sessionFromContext(ctx) 277 if sess == nil && db.client.sessionPool != nil { 278 sess = session.NewImplicitClientSession(db.client.sessionPool, db.client.id) 279 defer sess.EndSession() 280 } 281 282 err := db.client.validSession(sess) 283 if err != nil { 284 return err 285 } 286 287 wc := db.writeConcern 288 if sess.TransactionRunning() { 289 wc = nil 290 } 291 if !writeconcern.AckWrite(wc) { 292 sess = nil 293 } 294 295 selector := makePinnedSelector(sess, db.writeSelector) 296 297 op := operation.NewDropDatabase(). 298 Session(sess).WriteConcern(wc).CommandMonitor(db.client.monitor). 299 ServerSelector(selector).ClusterClock(db.client.clock). 300 Database(db.name).Deployment(db.client.deployment).Crypt(db.client.cryptFLE). 301 ServerAPI(db.client.serverAPI) 302 303 err = op.Execute(ctx) 304 305 driverErr, ok := err.(driver.Error) 306 if err != nil && (!ok || !driverErr.NamespaceNotFound()) { 307 return replaceErrors(err) 308 } 309 return nil 310 } 311 312 // ListCollectionSpecifications executes a listCollections command and returns a slice of CollectionSpecification 313 // instances representing the collections in the database. 314 // 315 // The filter parameter must be a document containing query operators and can be used to select which collections 316 // are included in the result. It cannot be nil. An empty document (e.g. bson.D{}) should be used to include all 317 // collections. 318 // 319 // The opts parameter can be used to specify options for the operation (see the options.ListCollectionsOptions 320 // documentation). 321 // 322 // For more information about the command, see https://www.mongodb.com/docs/manual/reference/command/listCollections/. 323 // 324 // BUG(benjirewis): ListCollectionSpecifications prevents listing more than 100 collections per database when running 325 // against MongoDB version 2.6. 326 func (db *Database) ListCollectionSpecifications(ctx context.Context, filter interface{}, 327 opts ...*options.ListCollectionsOptions) ([]*CollectionSpecification, error) { 328 329 cursor, err := db.ListCollections(ctx, filter, opts...) 330 if err != nil { 331 return nil, err 332 } 333 334 var specs []*CollectionSpecification 335 err = cursor.All(ctx, &specs) 336 if err != nil { 337 return nil, err 338 } 339 340 for _, spec := range specs { 341 // Pre-4.4 servers report a namespace in their responses, so we only set Namespace manually if it was not in 342 // the response. 343 if spec.IDIndex != nil && spec.IDIndex.Namespace == "" { 344 spec.IDIndex.Namespace = db.name + "." + spec.Name 345 } 346 } 347 return specs, nil 348 } 349 350 // ListCollections executes a listCollections command and returns a cursor over the collections in the database. 351 // 352 // The filter parameter must be a document containing query operators and can be used to select which collections 353 // are included in the result. It cannot be nil. An empty document (e.g. bson.D{}) should be used to include all 354 // collections. 355 // 356 // The opts parameter can be used to specify options for the operation (see the options.ListCollectionsOptions 357 // documentation). 358 // 359 // For more information about the command, see https://www.mongodb.com/docs/manual/reference/command/listCollections/. 360 // 361 // BUG(benjirewis): ListCollections prevents listing more than 100 collections per database when running against 362 // MongoDB version 2.6. 363 func (db *Database) ListCollections(ctx context.Context, filter interface{}, opts ...*options.ListCollectionsOptions) (*Cursor, error) { 364 if ctx == nil { 365 ctx = context.Background() 366 } 367 368 filterDoc, err := marshal(filter, db.bsonOpts, db.registry) 369 if err != nil { 370 return nil, err 371 } 372 373 sess := sessionFromContext(ctx) 374 if sess == nil && db.client.sessionPool != nil { 375 sess = session.NewImplicitClientSession(db.client.sessionPool, db.client.id) 376 } 377 378 err = db.client.validSession(sess) 379 if err != nil { 380 closeImplicitSession(sess) 381 return nil, err 382 } 383 384 selector := description.CompositeSelector([]description.ServerSelector{ 385 description.ReadPrefSelector(readpref.Primary()), 386 description.LatencySelector(db.client.localThreshold), 387 }) 388 selector = makeReadPrefSelector(sess, selector, db.client.localThreshold) 389 390 lco := options.MergeListCollectionsOptions(opts...) 391 op := operation.NewListCollections(filterDoc). 392 Session(sess).ReadPreference(db.readPreference).CommandMonitor(db.client.monitor). 393 ServerSelector(selector).ClusterClock(db.client.clock). 394 Database(db.name).Deployment(db.client.deployment).Crypt(db.client.cryptFLE). 395 ServerAPI(db.client.serverAPI).Timeout(db.client.timeout) 396 397 cursorOpts := db.client.createBaseCursorOptions() 398 if lco.NameOnly != nil { 399 op = op.NameOnly(*lco.NameOnly) 400 } 401 if lco.BatchSize != nil { 402 cursorOpts.BatchSize = *lco.BatchSize 403 op = op.BatchSize(*lco.BatchSize) 404 } 405 if lco.AuthorizedCollections != nil { 406 op = op.AuthorizedCollections(*lco.AuthorizedCollections) 407 } 408 409 retry := driver.RetryNone 410 if db.client.retryReads { 411 retry = driver.RetryOncePerCommand 412 } 413 op = op.Retry(retry) 414 415 err = op.Execute(ctx) 416 if err != nil { 417 closeImplicitSession(sess) 418 return nil, replaceErrors(err) 419 } 420 421 bc, err := op.Result(cursorOpts) 422 if err != nil { 423 closeImplicitSession(sess) 424 return nil, replaceErrors(err) 425 } 426 cursor, err := newCursorWithSession(bc, db.bsonOpts, db.registry, sess) 427 return cursor, replaceErrors(err) 428 } 429 430 // ListCollectionNames executes a listCollections command and returns a slice containing the names of the collections 431 // in the database. This method requires driver version >= 1.1.0. 432 // 433 // The filter parameter must be a document containing query operators and can be used to select which collections 434 // are included in the result. It cannot be nil. An empty document (e.g. bson.D{}) should be used to include all 435 // collections. 436 // 437 // The opts parameter can be used to specify options for the operation (see the options.ListCollectionsOptions 438 // documentation). 439 // 440 // For more information about the command, see https://www.mongodb.com/docs/manual/reference/command/listCollections/. 441 // 442 // BUG(benjirewis): ListCollectionNames prevents listing more than 100 collections per database when running against 443 // MongoDB version 2.6. 444 func (db *Database) ListCollectionNames(ctx context.Context, filter interface{}, opts ...*options.ListCollectionsOptions) ([]string, error) { 445 opts = append(opts, options.ListCollections().SetNameOnly(true)) 446 447 res, err := db.ListCollections(ctx, filter, opts...) 448 if err != nil { 449 return nil, err 450 } 451 452 defer res.Close(ctx) 453 454 names := make([]string, 0) 455 for res.Next(ctx) { 456 elem, err := res.Current.LookupErr("name") 457 if err != nil { 458 return nil, err 459 } 460 461 if elem.Type != bson.TypeString { 462 return nil, fmt.Errorf("incorrect type for 'name'. got %v. want %v", elem.Type, bson.TypeString) 463 } 464 465 elemName := elem.StringValue() 466 names = append(names, elemName) 467 } 468 469 res.Close(ctx) 470 return names, nil 471 } 472 473 // ReadConcern returns the read concern used to configure the Database object. 474 func (db *Database) ReadConcern() *readconcern.ReadConcern { 475 return db.readConcern 476 } 477 478 // ReadPreference returns the read preference used to configure the Database object. 479 func (db *Database) ReadPreference() *readpref.ReadPref { 480 return db.readPreference 481 } 482 483 // WriteConcern returns the write concern used to configure the Database object. 484 func (db *Database) WriteConcern() *writeconcern.WriteConcern { 485 return db.writeConcern 486 } 487 488 // Watch returns a change stream for all changes to the corresponding database. See 489 // https://www.mongodb.com/docs/manual/changeStreams/ for more information about change streams. 490 // 491 // The Database must be configured with read concern majority or no read concern for a change stream to be created 492 // successfully. 493 // 494 // The pipeline parameter must be a slice of documents, each representing a pipeline stage. The pipeline cannot be 495 // nil but can be empty. The stage documents must all be non-nil. See https://www.mongodb.com/docs/manual/changeStreams/ for 496 // a list of pipeline stages that can be used with change streams. For a pipeline of bson.D documents, the 497 // mongo.Pipeline{} type can be used. 498 // 499 // The opts parameter can be used to specify options for change stream creation (see the options.ChangeStreamOptions 500 // documentation). 501 func (db *Database) Watch(ctx context.Context, pipeline interface{}, 502 opts ...*options.ChangeStreamOptions) (*ChangeStream, error) { 503 504 csConfig := changeStreamConfig{ 505 readConcern: db.readConcern, 506 readPreference: db.readPreference, 507 client: db.client, 508 registry: db.registry, 509 streamType: DatabaseStream, 510 databaseName: db.Name(), 511 crypt: db.client.cryptFLE, 512 } 513 return newChangeStream(ctx, csConfig, pipeline, opts...) 514 } 515 516 // CreateCollection executes a create command to explicitly create a new collection with the specified name on the 517 // server. If the collection being created already exists, this method will return a mongo.CommandError. This method 518 // requires driver version 1.4.0 or higher. 519 // 520 // The opts parameter can be used to specify options for the operation (see the options.CreateCollectionOptions 521 // documentation). 522 // 523 // For more information about the command, see https://www.mongodb.com/docs/manual/reference/command/create/. 524 func (db *Database) CreateCollection(ctx context.Context, name string, opts ...*options.CreateCollectionOptions) error { 525 cco := options.MergeCreateCollectionOptions(opts...) 526 // Follow Client-Side Encryption specification to check for encryptedFields. 527 // Check for encryptedFields from create options. 528 ef := cco.EncryptedFields 529 // Check for encryptedFields from the client EncryptedFieldsMap. 530 if ef == nil { 531 ef = db.getEncryptedFieldsFromMap(name) 532 } 533 if ef != nil { 534 return db.createCollectionWithEncryptedFields(ctx, name, ef, opts...) 535 } 536 537 return db.createCollection(ctx, name, opts...) 538 } 539 540 // getEncryptedFieldsFromServer tries to get an "encryptedFields" document associated with collectionName by running the "listCollections" command. 541 // Returns nil and no error if the listCollections command succeeds, but "encryptedFields" is not present. 542 func (db *Database) getEncryptedFieldsFromServer(ctx context.Context, collectionName string) (interface{}, error) { 543 // Check if collection has an EncryptedFields configured server-side. 544 collSpecs, err := db.ListCollectionSpecifications(ctx, bson.D{{"name", collectionName}}) 545 if err != nil { 546 return nil, err 547 } 548 if len(collSpecs) == 0 { 549 return nil, nil 550 } 551 if len(collSpecs) > 1 { 552 return nil, fmt.Errorf("expected 1 or 0 results from listCollections, got %v", len(collSpecs)) 553 } 554 collSpec := collSpecs[0] 555 rawValue, err := collSpec.Options.LookupErr("encryptedFields") 556 if err == bsoncore.ErrElementNotFound { 557 return nil, nil 558 } else if err != nil { 559 return nil, err 560 } 561 562 encryptedFields, ok := rawValue.DocumentOK() 563 if !ok { 564 return nil, fmt.Errorf("expected encryptedFields of %v to be document, got %v", collectionName, rawValue.Type) 565 } 566 567 return encryptedFields, nil 568 } 569 570 // getEncryptedFieldsFromServer tries to get an "encryptedFields" document associated with collectionName by checking the client EncryptedFieldsMap. 571 // Returns nil and no error if an EncryptedFieldsMap is not configured, or does not contain an entry for collectionName. 572 func (db *Database) getEncryptedFieldsFromMap(collectionName string) interface{} { 573 // Check the EncryptedFieldsMap 574 efMap := db.client.encryptedFieldsMap 575 if efMap == nil { 576 return nil 577 } 578 579 namespace := db.name + "." + collectionName 580 581 ef, ok := efMap[namespace] 582 if ok { 583 return ef 584 } 585 return nil 586 } 587 588 // createCollectionWithEncryptedFields creates a collection with an EncryptedFields. 589 func (db *Database) createCollectionWithEncryptedFields(ctx context.Context, name string, ef interface{}, opts ...*options.CreateCollectionOptions) error { 590 efBSON, err := marshal(ef, db.bsonOpts, db.registry) 591 if err != nil { 592 return fmt.Errorf("error transforming document: %v", err) 593 } 594 595 // Check the wire version to ensure server is 7.0.0 or newer. 596 // After the wire version check, and before creating the collections, it is possible the server state changes. 597 // That is OK. This wire version check is a best effort to inform users earlier if using a QEv2 driver with a QEv1 server. 598 { 599 const QEv2WireVersion = 21 600 server, err := db.client.deployment.SelectServer(ctx, description.WriteSelector()) 601 if err != nil { 602 return fmt.Errorf("error selecting server to check maxWireVersion: %w", err) 603 } 604 conn, err := server.Connection(ctx) 605 if err != nil { 606 return fmt.Errorf("error getting connection to check maxWireVersion: %w", err) 607 } 608 defer conn.Close() 609 wireVersionRange := conn.Description().WireVersion 610 if wireVersionRange.Max < QEv2WireVersion { 611 return fmt.Errorf("Driver support of Queryable Encryption is incompatible with server. Upgrade server to use Queryable Encryption. Got maxWireVersion %v but need maxWireVersion >= %v", wireVersionRange.Max, QEv2WireVersion) 612 } 613 } 614 615 // Create the two encryption-related, associated collections: `escCollection` and `ecocCollection`. 616 617 stateCollectionOpts := options.CreateCollection(). 618 SetClusteredIndex(bson.D{{"key", bson.D{{"_id", 1}}}, {"unique", true}}) 619 // Create ESCCollection. 620 escCollection, err := internal.GetEncryptedStateCollectionName(efBSON, name, internal.EncryptedStateCollection) 621 if err != nil { 622 return err 623 } 624 625 if err := db.createCollection(ctx, escCollection, stateCollectionOpts); err != nil { 626 return err 627 } 628 629 // Create ECOCCollection. 630 ecocCollection, err := internal.GetEncryptedStateCollectionName(efBSON, name, internal.EncryptedCompactionCollection) 631 if err != nil { 632 return err 633 } 634 635 if err := db.createCollection(ctx, ecocCollection, stateCollectionOpts); err != nil { 636 return err 637 } 638 639 // Create a data collection with the 'encryptedFields' option. 640 op, err := db.createCollectionOperation(name, opts...) 641 if err != nil { 642 return err 643 } 644 645 op.EncryptedFields(efBSON) 646 if err := db.executeCreateOperation(ctx, op); err != nil { 647 return err 648 } 649 650 // Create an index on the __safeContent__ field in the collection @collectionName. 651 if _, err := db.Collection(name).Indexes().CreateOne(ctx, IndexModel{Keys: bson.D{{"__safeContent__", 1}}}); err != nil { 652 return fmt.Errorf("error creating safeContent index: %v", err) 653 } 654 655 return nil 656 } 657 658 // createCollection creates a collection without EncryptedFields. 659 func (db *Database) createCollection(ctx context.Context, name string, opts ...*options.CreateCollectionOptions) error { 660 op, err := db.createCollectionOperation(name, opts...) 661 if err != nil { 662 return err 663 } 664 return db.executeCreateOperation(ctx, op) 665 } 666 667 func (db *Database) createCollectionOperation(name string, opts ...*options.CreateCollectionOptions) (*operation.Create, error) { 668 cco := options.MergeCreateCollectionOptions(opts...) 669 op := operation.NewCreate(name).ServerAPI(db.client.serverAPI) 670 671 if cco.Capped != nil { 672 op.Capped(*cco.Capped) 673 } 674 if cco.Collation != nil { 675 op.Collation(bsoncore.Document(cco.Collation.ToDocument())) 676 } 677 if cco.ChangeStreamPreAndPostImages != nil { 678 csppi, err := marshal(cco.ChangeStreamPreAndPostImages, db.bsonOpts, db.registry) 679 if err != nil { 680 return nil, err 681 } 682 op.ChangeStreamPreAndPostImages(csppi) 683 } 684 if cco.DefaultIndexOptions != nil { 685 idx, doc := bsoncore.AppendDocumentStart(nil) 686 if cco.DefaultIndexOptions.StorageEngine != nil { 687 storageEngine, err := marshal(cco.DefaultIndexOptions.StorageEngine, db.bsonOpts, db.registry) 688 if err != nil { 689 return nil, err 690 } 691 692 doc = bsoncore.AppendDocumentElement(doc, "storageEngine", storageEngine) 693 } 694 doc, err := bsoncore.AppendDocumentEnd(doc, idx) 695 if err != nil { 696 return nil, err 697 } 698 699 op.IndexOptionDefaults(doc) 700 } 701 if cco.MaxDocuments != nil { 702 op.Max(*cco.MaxDocuments) 703 } 704 if cco.SizeInBytes != nil { 705 op.Size(*cco.SizeInBytes) 706 } 707 if cco.StorageEngine != nil { 708 storageEngine, err := marshal(cco.StorageEngine, db.bsonOpts, db.registry) 709 if err != nil { 710 return nil, err 711 } 712 op.StorageEngine(storageEngine) 713 } 714 if cco.ValidationAction != nil { 715 op.ValidationAction(*cco.ValidationAction) 716 } 717 if cco.ValidationLevel != nil { 718 op.ValidationLevel(*cco.ValidationLevel) 719 } 720 if cco.Validator != nil { 721 validator, err := marshal(cco.Validator, db.bsonOpts, db.registry) 722 if err != nil { 723 return nil, err 724 } 725 op.Validator(validator) 726 } 727 if cco.ExpireAfterSeconds != nil { 728 op.ExpireAfterSeconds(*cco.ExpireAfterSeconds) 729 } 730 if cco.TimeSeriesOptions != nil { 731 idx, doc := bsoncore.AppendDocumentStart(nil) 732 doc = bsoncore.AppendStringElement(doc, "timeField", cco.TimeSeriesOptions.TimeField) 733 734 if cco.TimeSeriesOptions.MetaField != nil { 735 doc = bsoncore.AppendStringElement(doc, "metaField", *cco.TimeSeriesOptions.MetaField) 736 } 737 if cco.TimeSeriesOptions.Granularity != nil { 738 doc = bsoncore.AppendStringElement(doc, "granularity", *cco.TimeSeriesOptions.Granularity) 739 } 740 741 if cco.TimeSeriesOptions.BucketMaxSpan != nil { 742 bmss := int64(*cco.TimeSeriesOptions.BucketMaxSpan / time.Second) 743 744 doc = bsoncore.AppendInt64Element(doc, "bucketMaxSpanSeconds", bmss) 745 } 746 747 if cco.TimeSeriesOptions.BucketRounding != nil { 748 brs := int64(*cco.TimeSeriesOptions.BucketRounding / time.Second) 749 750 doc = bsoncore.AppendInt64Element(doc, "bucketRoundingSeconds", brs) 751 } 752 753 doc, err := bsoncore.AppendDocumentEnd(doc, idx) 754 if err != nil { 755 return nil, err 756 } 757 758 op.TimeSeries(doc) 759 } 760 if cco.ClusteredIndex != nil { 761 clusteredIndex, err := marshal(cco.ClusteredIndex, db.bsonOpts, db.registry) 762 if err != nil { 763 return nil, err 764 } 765 op.ClusteredIndex(clusteredIndex) 766 } 767 768 return op, nil 769 } 770 771 // CreateView executes a create command to explicitly create a view on the server. See 772 // https://www.mongodb.com/docs/manual/core/views/ for more information about views. This method requires driver version >= 773 // 1.4.0 and MongoDB version >= 3.4. 774 // 775 // The viewName parameter specifies the name of the view to create. 776 // 777 // # The viewOn parameter specifies the name of the collection or view on which this view will be created 778 // 779 // The pipeline parameter specifies an aggregation pipeline that will be exececuted against the source collection or 780 // view to create this view. 781 // 782 // The opts parameter can be used to specify options for the operation (see the options.CreateViewOptions 783 // documentation). 784 func (db *Database) CreateView(ctx context.Context, viewName, viewOn string, pipeline interface{}, 785 opts ...*options.CreateViewOptions) error { 786 787 pipelineArray, _, err := marshalAggregatePipeline(pipeline, db.bsonOpts, db.registry) 788 if err != nil { 789 return err 790 } 791 792 op := operation.NewCreate(viewName). 793 ViewOn(viewOn). 794 Pipeline(pipelineArray). 795 ServerAPI(db.client.serverAPI) 796 cvo := options.MergeCreateViewOptions(opts...) 797 if cvo.Collation != nil { 798 op.Collation(bsoncore.Document(cvo.Collation.ToDocument())) 799 } 800 801 return db.executeCreateOperation(ctx, op) 802 } 803 804 func (db *Database) executeCreateOperation(ctx context.Context, op *operation.Create) error { 805 sess := sessionFromContext(ctx) 806 if sess == nil && db.client.sessionPool != nil { 807 sess = session.NewImplicitClientSession(db.client.sessionPool, db.client.id) 808 defer sess.EndSession() 809 } 810 811 err := db.client.validSession(sess) 812 if err != nil { 813 return err 814 } 815 816 wc := db.writeConcern 817 if sess.TransactionRunning() { 818 wc = nil 819 } 820 if !writeconcern.AckWrite(wc) { 821 sess = nil 822 } 823 824 selector := makePinnedSelector(sess, db.writeSelector) 825 op = op.Session(sess). 826 WriteConcern(wc). 827 CommandMonitor(db.client.monitor). 828 ServerSelector(selector). 829 ClusterClock(db.client.clock). 830 Database(db.name). 831 Deployment(db.client.deployment). 832 Crypt(db.client.cryptFLE) 833 834 return replaceErrors(op.Execute(ctx)) 835 }