@@ -507,6 +507,158 @@ func (db *Database) Watch(ctx context.Context, pipeline interface{},
507
507
//
508
508
// For more information about the command, see https://www.mongodb.com/docs/manual/reference/command/create/.
509
509
func (db * Database ) CreateCollection (ctx context.Context , name string , opts ... * options.CreateCollectionOptions ) error {
510
+ cco := options .MergeCreateCollectionOptions (opts ... )
511
+ // Follow Client-Side Encryption specification to check for encryptedFields.
512
+ // Check for encryptedFields from create options.
513
+ ef := cco .EncryptedFields
514
+ // Check for encryptedFields from the client EncryptedFieldsMap.
515
+ if ef == nil {
516
+ ef = db .getEncryptedFieldsFromMap (name )
517
+ }
518
+ if ef != nil {
519
+ return db .createCollectionWithEncryptedFields (ctx , name , ef , opts ... )
520
+ }
521
+
522
+ return db .createCollection (ctx , name , opts ... )
523
+ }
524
+
525
+ // getEncryptedFieldsFromServer tries to get an "encryptedFields" document associated with collectionName by running the "listCollections" command.
526
+ // Returns nil and no error if the listCollections command succeeds, but "encryptedFields" is not present.
527
+ func (db * Database ) getEncryptedFieldsFromServer (ctx context.Context , collectionName string ) (interface {}, error ) {
528
+ // Check if collection has an EncryptedFields configured server-side.
529
+ collSpecs , err := db .ListCollectionSpecifications (ctx , bson.D {{"name" , collectionName }})
530
+ if err != nil {
531
+ return nil , err
532
+ }
533
+ if len (collSpecs ) == 0 {
534
+ return nil , nil
535
+ }
536
+ if len (collSpecs ) > 1 {
537
+ return nil , fmt .Errorf ("expected 1 or 0 results from listCollections, got %v" , len (collSpecs ))
538
+ }
539
+ collSpec := collSpecs [0 ]
540
+ rawValue , err := collSpec .Options .LookupErr ("encryptedFields" )
541
+ if err == bsoncore .ErrElementNotFound {
542
+ return nil , nil
543
+ } else if err != nil {
544
+ return nil , err
545
+ }
546
+
547
+ encryptedFields , ok := rawValue .DocumentOK ()
548
+ if ! ok {
549
+ return nil , fmt .Errorf ("expected encryptedFields of %v to be document, got %v" , collectionName , rawValue .Type )
550
+ }
551
+
552
+ return encryptedFields , nil
553
+ }
554
+
555
+ // getEncryptedFieldsFromServer tries to get an "encryptedFields" document associated with collectionName by checking the client EncryptedFieldsMap.
556
+ // Returns nil and no error if an EncryptedFieldsMap is not configured, or does not contain an entry for collectionName.
557
+ func (db * Database ) getEncryptedFieldsFromMap (collectionName string ) interface {} {
558
+ // Check the EncryptedFieldsMap
559
+ efMap := db .client .encryptedFieldsMap
560
+ if efMap == nil {
561
+ return nil
562
+ }
563
+
564
+ namespace := db .name + "." + collectionName
565
+
566
+ ef , ok := efMap [namespace ]
567
+ if ok {
568
+ return ef
569
+ }
570
+ return nil
571
+ }
572
+
573
+ // getEncryptedStateCollectionName returns the encrypted state collection name associated with dataCollectionName.
574
+ func getEncryptedStateCollectionName (efBSON bsoncore.Document , dataCollectionName string , stateCollectionSuffix string ) (string , error ) {
575
+ if stateCollectionSuffix != "esc" && stateCollectionSuffix != "ecc" && stateCollectionSuffix != "ecoc" {
576
+ return "" , fmt .Errorf ("expected stateCollectionSuffix: esc, ecc, or ecoc. got %v" , stateCollectionSuffix )
577
+ }
578
+ fieldName := stateCollectionSuffix + "Collection"
579
+ var val bsoncore.Value
580
+ var err error
581
+ if val , err = efBSON .LookupErr (fieldName ); err != nil {
582
+ if err != bsoncore .ErrElementNotFound {
583
+ return "" , err
584
+ }
585
+ // Return default name.
586
+ defaultName := "enxcol_." + dataCollectionName + "." + stateCollectionSuffix
587
+ return defaultName , nil
588
+ }
589
+
590
+ var stateCollectionName string
591
+ var ok bool
592
+ if stateCollectionName , ok = val .StringValueOK (); ! ok {
593
+ return "" , fmt .Errorf ("expected string for '%v', got: %v" , fieldName , val .Type )
594
+ }
595
+ return stateCollectionName , nil
596
+ }
597
+
598
+ // createCollectionWithEncryptedFields creates a collection with an EncryptedFields.
599
+ func (db * Database ) createCollectionWithEncryptedFields (ctx context.Context , name string , ef interface {}, opts ... * options.CreateCollectionOptions ) error {
600
+ efBSON , err := transformBsoncoreDocument (db .registry , ef , true /* mapAllowed */ , "encryptedFields" )
601
+ if err != nil {
602
+ return fmt .Errorf ("error transforming document: %v" , err )
603
+ }
604
+
605
+ // Create the three encryption-related, associated collections: `escCollection`, `eccCollection` and `ecocCollection`.
606
+ // Create ESCCollection.
607
+ escCollection , err := getEncryptedStateCollectionName (efBSON , name , "esc" )
608
+ if err != nil {
609
+ return err
610
+ }
611
+ if err := db .createCollection (ctx , escCollection ); err != nil {
612
+ return err
613
+ }
614
+
615
+ // Create ECCCollection.
616
+ eccCollection , err := getEncryptedStateCollectionName (efBSON , name , "ecc" )
617
+ if err != nil {
618
+ return err
619
+ }
620
+ if err := db .createCollection (ctx , eccCollection ); err != nil {
621
+ return err
622
+ }
623
+
624
+ // Create ECOCCollection.
625
+ ecocCollection , err := getEncryptedStateCollectionName (efBSON , name , "ecoc" )
626
+ if err != nil {
627
+ return err
628
+ }
629
+ if err := db .createCollection (ctx , ecocCollection ); err != nil {
630
+ return err
631
+ }
632
+
633
+ // Create a data collection with the 'encryptedFields' option.
634
+ op , err := db .createCollectionOperation (name , opts ... )
635
+ if err != nil {
636
+ return err
637
+ }
638
+
639
+ op .EncryptedFields (efBSON )
640
+ if err := db .executeCreateOperation (ctx , op ); err != nil {
641
+ return err
642
+ }
643
+
644
+ // Create an index on the __safeContent__ field in the collection @collectionName.
645
+ if _ , err := db .Collection (name ).Indexes ().CreateOne (ctx , IndexModel {Keys : bson.D {{"__safeContent__" , 1 }}}); err != nil {
646
+ return fmt .Errorf ("error creating safeContent index: %v" , err )
647
+ }
648
+
649
+ return nil
650
+ }
651
+
652
+ // createCollection creates a collection without EncryptedFields.
653
+ func (db * Database ) createCollection (ctx context.Context , name string , opts ... * options.CreateCollectionOptions ) error {
654
+ op , err := db .createCollectionOperation (name , opts ... )
655
+ if err != nil {
656
+ return err
657
+ }
658
+ return db .executeCreateOperation (ctx , op )
659
+ }
660
+
661
+ func (db * Database ) createCollectionOperation (name string , opts ... * options.CreateCollectionOptions ) (* operation.Create , error ) {
510
662
cco := options .MergeCreateCollectionOptions (opts ... )
511
663
op := operation .NewCreate (name ).ServerAPI (db .client .serverAPI )
512
664
@@ -519,7 +671,7 @@ func (db *Database) CreateCollection(ctx context.Context, name string, opts ...*
519
671
if cco .ChangeStreamPreAndPostImages != nil {
520
672
csppi , err := transformBsoncoreDocument (db .registry , cco .ChangeStreamPreAndPostImages , true , "changeStreamPreAndPostImages" )
521
673
if err != nil {
522
- return err
674
+ return nil , err
523
675
}
524
676
op .ChangeStreamPreAndPostImages (csppi )
525
677
}
@@ -528,14 +680,14 @@ func (db *Database) CreateCollection(ctx context.Context, name string, opts ...*
528
680
if cco .DefaultIndexOptions .StorageEngine != nil {
529
681
storageEngine , err := transformBsoncoreDocument (db .registry , cco .DefaultIndexOptions .StorageEngine , true , "storageEngine" )
530
682
if err != nil {
531
- return err
683
+ return nil , err
532
684
}
533
685
534
686
doc = bsoncore .AppendDocumentElement (doc , "storageEngine" , storageEngine )
535
687
}
536
688
doc , err := bsoncore .AppendDocumentEnd (doc , idx )
537
689
if err != nil {
538
- return err
690
+ return nil , err
539
691
}
540
692
541
693
op .IndexOptionDefaults (doc )
@@ -549,7 +701,7 @@ func (db *Database) CreateCollection(ctx context.Context, name string, opts ...*
549
701
if cco .StorageEngine != nil {
550
702
storageEngine , err := transformBsoncoreDocument (db .registry , cco .StorageEngine , true , "storageEngine" )
551
703
if err != nil {
552
- return err
704
+ return nil , err
553
705
}
554
706
op .StorageEngine (storageEngine )
555
707
}
@@ -562,7 +714,7 @@ func (db *Database) CreateCollection(ctx context.Context, name string, opts ...*
562
714
if cco .Validator != nil {
563
715
validator , err := transformBsoncoreDocument (db .registry , cco .Validator , true , "validator" )
564
716
if err != nil {
565
- return err
717
+ return nil , err
566
718
}
567
719
op .Validator (validator )
568
720
}
@@ -582,13 +734,13 @@ func (db *Database) CreateCollection(ctx context.Context, name string, opts ...*
582
734
583
735
doc , err := bsoncore .AppendDocumentEnd (doc , idx )
584
736
if err != nil {
585
- return err
737
+ return nil , err
586
738
}
587
739
588
740
op .TimeSeries (doc )
589
741
}
590
742
591
- return db . executeCreateOperation ( ctx , op )
743
+ return op , nil
592
744
}
593
745
594
746
// CreateView executes a create command to explicitly create a view on the server. See
0 commit comments