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  }