diff --git a/mongo/bulk_write.go b/mongo/bulk_write.go index 40f1181e0e..81dfbb1d8e 100644 --- a/mongo/bulk_write.go +++ b/mongo/bulk_write.go @@ -39,6 +39,7 @@ type bulkWrite struct { writeConcern *writeconcern.WriteConcern result BulkWriteResult let interface{} + bypassEmptyTsReplacement *bool } func (bw *bulkWrite) execute(ctx context.Context) error { @@ -207,6 +208,10 @@ func (bw *bulkWrite) runInsert(ctx context.Context, batch bulkWriteBatch) (opera } op = op.Retry(retry) + if bw.bypassEmptyTsReplacement != nil { + op.BypassEmptyTsReplacement(*bw.bypassEmptyTsReplacement) + } + err := op.Execute(ctx) return op.Result(), err @@ -415,6 +420,10 @@ func (bw *bulkWrite) runUpdate(ctx context.Context, batch bulkWriteBatch) (opera } op = op.Retry(retry) + if bw.bypassEmptyTsReplacement != nil { + op.BypassEmptyTsReplacement(*bw.bypassEmptyTsReplacement) + } + err := op.Execute(ctx) return op.Result(), err diff --git a/mongo/collection.go b/mongo/collection.go index dbe238a9e3..95889c80a4 100644 --- a/mongo/collection.go +++ b/mongo/collection.go @@ -234,6 +234,7 @@ func (coll *Collection) BulkWrite(ctx context.Context, models []WriteModel, selector: selector, writeConcern: wc, let: bwo.Let, + bypassEmptyTsReplacement: bwo.BypassEmptyTsReplacement, } err = op.execute(ctx) @@ -307,6 +308,9 @@ func (coll *Collection) insert(ctx context.Context, documents []interface{}, if imo.Ordered != nil { op = op.Ordered(*imo.Ordered) } + if imo.BypassEmptyTsReplacement != nil { + op = op.BypassEmptyTsReplacement(*imo.BypassEmptyTsReplacement) + } retry := driver.RetryNone if coll.client.retryWrites { retry = driver.RetryOncePerCommand @@ -355,6 +359,9 @@ func (coll *Collection) InsertOne(ctx context.Context, document interface{}, if ioOpts.Comment != nil { imOpts.SetComment(ioOpts.Comment) } + if ioOpts.BypassEmptyTsReplacement != nil { + imOpts.BypassEmptyTsReplacement = ioOpts.BypassEmptyTsReplacement + } res, err := coll.insert(ctx, []interface{}{document}, imOpts) rr, err := processWriteError(err) @@ -609,6 +616,9 @@ func (coll *Collection) updateOrReplace(ctx context.Context, filter bsoncore.Doc } op = op.Comment(comment) } + if uo.BypassEmptyTsReplacement != nil { + op.BypassEmptyTsReplacement(*uo.BypassEmptyTsReplacement) + } retry := driver.RetryNone // retryable writes are only enabled updateOne/replaceOne operations if !multi && coll.client.retryWrites { @@ -760,6 +770,7 @@ func (coll *Collection) ReplaceOne(ctx context.Context, filter interface{}, uOpts.Hint = opt.Hint uOpts.Let = opt.Let uOpts.Comment = opt.Comment + uOpts.BypassEmptyTsReplacement = opt.BypassEmptyTsReplacement updateOptions = append(updateOptions, uOpts) } @@ -1659,6 +1670,9 @@ func (coll *Collection) FindOneAndReplace(ctx context.Context, filter interface{ } op = op.Let(let) } + if fo.BypassEmptyTsReplacement != nil { + op = op.BypassEmptyTsReplacement(*fo.BypassEmptyTsReplacement) + } return coll.findAndModify(ctx, op) } @@ -1765,6 +1779,9 @@ func (coll *Collection) FindOneAndUpdate(ctx context.Context, filter interface{} } op = op.Let(let) } + if fo.BypassEmptyTsReplacement != nil { + op = op.BypassEmptyTsReplacement(*fo.BypassEmptyTsReplacement) + } return coll.findAndModify(ctx, op) } diff --git a/mongo/integration/collection_test.go b/mongo/integration/collection_test.go index 15c0e9324c..d04fde0be2 100644 --- a/mongo/integration/collection_test.go +++ b/mongo/integration/collection_test.go @@ -9,6 +9,7 @@ package integration import ( "context" "errors" + "fmt" "strings" "testing" "time" @@ -17,6 +18,7 @@ import ( "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/event" "go.mongodb.org/mongo-driver/internal/assert" + "go.mongodb.org/mongo-driver/internal/require" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/integration/mtest" "go.mongodb.org/mongo-driver/mongo/options" @@ -1861,6 +1863,334 @@ func TestCollection(t *testing.T) { }) } +func TestBypassEmptyTsReplacement(t *testing.T) { + mt := mtest.New(t, mtest.NewOptions().CreateClient(false).MinServerVersion("5.0")) + + marshalValue := func(val interface{}) bson.RawValue { + t.Helper() + + valType, data, err := bson.MarshalValue(val) + require.Nil(t, err, "MarshalValue error: %v", err) + return bson.RawValue{ + Type: valType, + Value: data, + } + } + + boolPtr := func(b bool) *bool { + return &b + } + + mt.Run("insert one", func(mt *mtest.T) { + doc := bson.D{{"x", 42}} + + testCases := []struct { + name string + opts *options.InsertOneOptions + expected bson.RawValue + }{ + { + "empty", + nil, + bson.RawValue{}, + }, + { + "false", + &options.InsertOneOptions{BypassEmptyTsReplacement: boolPtr(false)}, + marshalValue(false), + }, + { + "true", + &options.InsertOneOptions{BypassEmptyTsReplacement: boolPtr(true)}, + marshalValue(true), + }, + } + for _, tc := range testCases { + mt.Run(tc.name, func(mt *mtest.T) { + _, err := mt.Coll.InsertOne(context.Background(), doc, tc.opts) + require.NoError(mt, err, "InsertOne error: %v", err) + evt := mt.GetStartedEvent() + val := evt.Command.Lookup("bypassEmptyTsReplacement") + assert.Equal(mt, tc.expected, val, "expected bypassEmptyTsReplacement to be %s", tc.expected.String()) + }) + } + }) + mt.Run("insert many", func(mt *mtest.T) { + docs := []interface{}{ + bson.D{{"x", 42}}, + bson.D{{"y", "foo"}}, + } + + testCases := []struct { + name string + opts *options.InsertManyOptions + expected bson.RawValue + }{ + { + "empty", + nil, + bson.RawValue{}, + }, + { + "false", + &options.InsertManyOptions{BypassEmptyTsReplacement: boolPtr(false)}, + marshalValue(false), + }, + { + "true", + &options.InsertManyOptions{BypassEmptyTsReplacement: boolPtr(true)}, + marshalValue(true), + }, + } + for _, tc := range testCases { + mt.Run(tc.name, func(mt *mtest.T) { + _, err := mt.Coll.InsertMany(context.Background(), docs, tc.opts) + require.NoError(mt, err, "InsertMany error: %v", err) + evt := mt.GetStartedEvent() + val := evt.Command.Lookup("bypassEmptyTsReplacement") + assert.Equal(mt, tc.expected, val, "expected bypassEmptyTsReplacement to be %s", tc.expected.String()) + }) + } + }) + mt.Run("update one", func(mt *mtest.T) { + filter := bson.D{{"x", 42}} + update := bson.D{{"$inc", bson.D{{"x", 1}}}} + + testCases := []struct { + name string + opts *options.UpdateOptions + expected bson.RawValue + }{ + { + "empty", + nil, + bson.RawValue{}, + }, + { + "false", + &options.UpdateOptions{BypassEmptyTsReplacement: boolPtr(false)}, + marshalValue(false), + }, + { + "true", + &options.UpdateOptions{BypassEmptyTsReplacement: boolPtr(true)}, + marshalValue(true), + }, + } + for _, tc := range testCases { + mt.Run(tc.name, func(mt *mtest.T) { + _, err := mt.Coll.UpdateOne(context.Background(), filter, update, tc.opts) + require.NoError(mt, err, "UpdateOne error: %v", err) + evt := mt.GetStartedEvent() + val := evt.Command.Lookup("bypassEmptyTsReplacement") + assert.Equal(mt, tc.expected, val, "expected bypassEmptyTsReplacement to be %s", tc.expected.String()) + }) + } + }) + mt.Run("update many", func(mt *mtest.T) { + filter := bson.D{{"x", 42}} + update := bson.D{{"$inc", bson.D{{"x", 1}}}} + + testCases := []struct { + name string + opts *options.UpdateOptions + expected bson.RawValue + }{ + { + "empty", + nil, + bson.RawValue{}, + }, + { + "false", + &options.UpdateOptions{BypassEmptyTsReplacement: boolPtr(false)}, + marshalValue(false), + }, + { + "true", + &options.UpdateOptions{BypassEmptyTsReplacement: boolPtr(true)}, + marshalValue(true), + }, + } + for _, tc := range testCases { + mt.Run(tc.name, func(mt *mtest.T) { + _, err := mt.Coll.UpdateMany(context.Background(), filter, update, tc.opts) + require.NoError(mt, err, "UpdateMany error: %v", err) + evt := mt.GetStartedEvent() + val := evt.Command.Lookup("bypassEmptyTsReplacement") + assert.Equal(mt, tc.expected, val, "expected bypassEmptyTsReplacement to be %s", tc.expected.String()) + }) + } + }) + mt.Run("replace one", func(mt *mtest.T) { + filter := bson.D{{"x", 42}} + replacement := bson.D{{"y", "foo"}} + + testCases := []struct { + name string + opts *options.ReplaceOptions + expected bson.RawValue + }{ + { + "empty", + nil, + bson.RawValue{}, + }, + { + "false", + &options.ReplaceOptions{BypassEmptyTsReplacement: boolPtr(false)}, + marshalValue(false), + }, + { + "true", + &options.ReplaceOptions{BypassEmptyTsReplacement: boolPtr(true)}, + marshalValue(true), + }, + } + for _, tc := range testCases { + mt.Run(tc.name, func(mt *mtest.T) { + _, err := mt.Coll.ReplaceOne(context.Background(), filter, replacement, tc.opts) + require.NoError(mt, err, "ReplaceOne error: %v", err) + evt := mt.GetStartedEvent() + val := evt.Command.Lookup("bypassEmptyTsReplacement") + assert.Equal(mt, tc.expected, val, "expected bypassEmptyTsReplacement to be %s", tc.expected.String()) + }) + } + }) + mt.Run("find one and update", func(mt *mtest.T) { + filter := bson.D{{"x", 1}} + update := bson.D{{"$inc", bson.D{{"x", 1}}}} + + testCases := []struct { + name string + opts *options.FindOneAndUpdateOptions + expected bson.RawValue + }{ + { + "empty", + nil, + bson.RawValue{}, + }, + { + "false", + &options.FindOneAndUpdateOptions{BypassEmptyTsReplacement: boolPtr(false)}, + marshalValue(false), + }, + { + "true", + &options.FindOneAndUpdateOptions{BypassEmptyTsReplacement: boolPtr(true)}, + marshalValue(true), + }, + } + for _, tc := range testCases { + mt.Run(tc.name, func(mt *mtest.T) { + initCollection(mt, mt.Coll) + mt.ClearEvents() + + _, err := mt.Coll.FindOneAndUpdate(context.Background(), filter, update, tc.opts).Raw() + require.NoError(mt, err, "FindOneAndUpdate error: %v", err) + evt := mt.GetStartedEvent() + val := evt.Command.Lookup("bypassEmptyTsReplacement") + assert.Equal(mt, tc.expected, val, "expected bypassEmptyTsReplacement to be %s", tc.expected.String()) + }) + } + }) + mt.Run("find one and replace", func(mt *mtest.T) { + filter := bson.D{{"x", 1}} + replacement := bson.D{{"y", "foo"}} + + testCases := []struct { + name string + opts *options.FindOneAndReplaceOptions + expected bson.RawValue + }{ + { + "empty", + nil, + bson.RawValue{}, + }, + { + "false", + &options.FindOneAndReplaceOptions{BypassEmptyTsReplacement: boolPtr(false)}, + marshalValue(false), + }, + { + "true", + &options.FindOneAndReplaceOptions{BypassEmptyTsReplacement: boolPtr(true)}, + marshalValue(true), + }, + } + for _, tc := range testCases { + mt.Run(tc.name, func(mt *mtest.T) { + initCollection(mt, mt.Coll) + mt.ClearEvents() + + _, err := mt.Coll.FindOneAndReplace(context.Background(), filter, replacement, tc.opts).Raw() + require.NoError(mt, err, "FindOneAndReplace error: %v", err) + evt := mt.GetStartedEvent() + val := evt.Command.Lookup("bypassEmptyTsReplacement") + assert.Equal(mt, tc.expected, val, "expected bypassEmptyTsReplacement to be %s", tc.expected.String()) + }) + } + }) + mt.Run("bulk write", func(mt *mtest.T) { + models := []struct { + name string + model mongo.WriteModel + }{ + { + "insert one", + mongo.NewInsertOneModel().SetDocument(bson.D{{"_id", "id1"}}), + }, + { + "update one", + mongo.NewUpdateOneModel().SetFilter(bson.D{{"_id", "id3"}}).SetUpdate(bson.D{{"$set", bson.D{{"_id", 3.14159}}}}), + }, + { + "update many", + mongo.NewUpdateManyModel().SetFilter(bson.D{{"_id", "id3"}}).SetUpdate(bson.D{{"$set", bson.D{{"_id", 3.14159}}}}), + }, + { + "replace one", + mongo.NewReplaceOneModel().SetFilter(bson.D{{"_id", "id3"}}).SetReplacement(bson.D{{"_id", 3.14159}}), + }, + } + + testCases := []struct { + name string + opts *options.BulkWriteOptions + expected bson.RawValue + }{ + { + "empty", + options.BulkWrite(), + bson.RawValue{}, + }, + { + "false", + &options.BulkWriteOptions{BypassEmptyTsReplacement: boolPtr(false)}, + marshalValue(false), + }, + { + "true", + &options.BulkWriteOptions{BypassEmptyTsReplacement: boolPtr(true)}, + marshalValue(true), + }, + } + for _, m := range models { + for _, tc := range testCases { + mt.Run(fmt.Sprintf("%s %s", m.name, tc.name), func(mt *mtest.T) { + _, err := mt.Coll.BulkWrite(context.Background(), []mongo.WriteModel{m.model}, tc.opts) + require.NoError(mt, err, "BulkWrite error: %v", err) + evt := mt.GetStartedEvent() + val := evt.Command.Lookup("bypassEmptyTsReplacement") + assert.Equal(mt, tc.expected, val, "expected bypassEmptyTsReplacement to be %s", tc.expected.String()) + }) + } + } + }) +} + func initCollection(mt *mtest.T, coll *mongo.Collection) { mt.Helper() diff --git a/mongo/options/bulkwriteoptions.go b/mongo/options/bulkwriteoptions.go index 153de0c735..49d7a0f5a5 100644 --- a/mongo/options/bulkwriteoptions.go +++ b/mongo/options/bulkwriteoptions.go @@ -29,6 +29,12 @@ type BulkWriteOptions struct { // parameter names to values. Values must be constant or closed expressions that do not reference document fields. // Parameters can then be accessed as variables in an aggregate expression context (e.g. "$$var"). Let interface{} + + // If true, the server accepts empty Timestamp as a literal rather than replacing it with the current time. + // + // Deprecated: This option is for internal use only and should not be set. It may be changed or removed in any + // release. + BypassEmptyTsReplacement *bool } // BulkWrite creates a new *BulkWriteOptions instance. @@ -88,6 +94,9 @@ func MergeBulkWriteOptions(opts ...*BulkWriteOptions) *BulkWriteOptions { if opt.Let != nil { b.Let = opt.Let } + if opt.BypassEmptyTsReplacement != nil { + b.BypassEmptyTsReplacement = opt.BypassEmptyTsReplacement + } } return b diff --git a/mongo/options/findoptions.go b/mongo/options/findoptions.go index fa3bf1197a..5795f95a43 100644 --- a/mongo/options/findoptions.go +++ b/mongo/options/findoptions.go @@ -675,6 +675,12 @@ type FindOneAndReplaceOptions struct { // Values must be constant or closed expressions that do not reference document fields. Parameters can then be // accessed as variables in an aggregate expression context (e.g. "$$var"). Let interface{} + + // If true, the server accepts empty Timestamp as a literal rather than replacing it with the current time. + // + // Deprecated: This option is for internal use only and should not be set. It may be changed or removed in any + // release. + BypassEmptyTsReplacement *bool } // FindOneAndReplace creates a new FindOneAndReplaceOptions instance. @@ -787,6 +793,9 @@ func MergeFindOneAndReplaceOptions(opts ...*FindOneAndReplaceOptions) *FindOneAn if opt.Let != nil { fo.Let = opt.Let } + if opt.BypassEmptyTsReplacement != nil { + fo.BypassEmptyTsReplacement = opt.BypassEmptyTsReplacement + } } return fo @@ -852,6 +861,12 @@ type FindOneAndUpdateOptions struct { // Values must be constant or closed expressions that do not reference document fields. Parameters can then be // accessed as variables in an aggregate expression context (e.g. "$$var"). Let interface{} + + // If true, the server accepts empty Timestamp as a literal rather than replacing it with the current time. + // + // Deprecated: This option is for internal use only and should not be set. It may be changed or removed in any + // release. + BypassEmptyTsReplacement *bool } // FindOneAndUpdate creates a new FindOneAndUpdateOptions instance. @@ -973,6 +988,9 @@ func MergeFindOneAndUpdateOptions(opts ...*FindOneAndUpdateOptions) *FindOneAndU if opt.Let != nil { fo.Let = opt.Let } + if opt.BypassEmptyTsReplacement != nil { + fo.BypassEmptyTsReplacement = opt.BypassEmptyTsReplacement + } } return fo diff --git a/mongo/options/insertoptions.go b/mongo/options/insertoptions.go index 82137c60a3..f84f799def 100644 --- a/mongo/options/insertoptions.go +++ b/mongo/options/insertoptions.go @@ -17,6 +17,12 @@ type InsertOneOptions struct { // A string or document that will be included in server logs, profiling logs, and currentOp queries to help trace // the operation. The default value is nil, which means that no comment will be included in the logs. Comment interface{} + + // If true, the server accepts empty Timestamp as a literal rather than replacing it with the current time. + // + // Deprecated: This option is for internal use only and should not be set. It may be changed or removed in any + // release. + BypassEmptyTsReplacement *bool } // InsertOne creates a new InsertOneOptions instance. @@ -53,6 +59,9 @@ func MergeInsertOneOptions(opts ...*InsertOneOptions) *InsertOneOptions { if ioo.Comment != nil { ioOpts.Comment = ioo.Comment } + if ioo.BypassEmptyTsReplacement != nil { + ioOpts.BypassEmptyTsReplacement = ioo.BypassEmptyTsReplacement + } } return ioOpts @@ -72,6 +81,12 @@ type InsertManyOptions struct { // If true, no writes will be executed after one fails. The default value is true. Ordered *bool + + // If true, the server accepts empty Timestamp as a literal rather than replacing it with the current time. + // + // Deprecated: This option is for internal use only and should not be set. It may be changed or removed in any + // release. + BypassEmptyTsReplacement *bool } // InsertMany creates a new InsertManyOptions instance. @@ -119,6 +134,9 @@ func MergeInsertManyOptions(opts ...*InsertManyOptions) *InsertManyOptions { if imo.Ordered != nil { imOpts.Ordered = imo.Ordered } + if imo.BypassEmptyTsReplacement != nil { + imOpts.BypassEmptyTsReplacement = imo.BypassEmptyTsReplacement + } } return imOpts diff --git a/mongo/options/replaceoptions.go b/mongo/options/replaceoptions.go index f7d3960194..ae3e784948 100644 --- a/mongo/options/replaceoptions.go +++ b/mongo/options/replaceoptions.go @@ -40,6 +40,12 @@ type ReplaceOptions struct { // Values must be constant or closed expressions that do not reference document fields. Parameters can then be // accessed as variables in an aggregate expression context (e.g. "$$var"). Let interface{} + + // If true, the server accepts empty Timestamp as a literal rather than replacing it with the current time. + // + // Deprecated: This option is for internal use only and should not be set. It may be changed or removed in any + // release. + BypassEmptyTsReplacement *bool } // Replace creates a new ReplaceOptions instance. @@ -112,6 +118,9 @@ func MergeReplaceOptions(opts ...*ReplaceOptions) *ReplaceOptions { if ro.Let != nil { rOpts.Let = ro.Let } + if ro.BypassEmptyTsReplacement != nil { + rOpts.BypassEmptyTsReplacement = ro.BypassEmptyTsReplacement + } } return rOpts diff --git a/mongo/options/updateoptions.go b/mongo/options/updateoptions.go index 5206f9f01b..9f22ca9042 100644 --- a/mongo/options/updateoptions.go +++ b/mongo/options/updateoptions.go @@ -45,6 +45,12 @@ type UpdateOptions struct { // Values must be constant or closed expressions that do not reference document fields. Parameters can then be // accessed as variables in an aggregate expression context (e.g. "$$var"). Let interface{} + + // If true, the server accepts empty Timestamp as a literal rather than replacing it with the current time. + // + // Deprecated: This option is for internal use only and should not be set. It may be changed or removed in any + // release. + BypassEmptyTsReplacement *bool } // Update creates a new UpdateOptions instance. @@ -125,6 +131,9 @@ func MergeUpdateOptions(opts ...*UpdateOptions) *UpdateOptions { if uo.Let != nil { uOpts.Let = uo.Let } + if uo.BypassEmptyTsReplacement != nil { + uOpts.BypassEmptyTsReplacement = uo.BypassEmptyTsReplacement + } } return uOpts diff --git a/x/mongo/driver/operation/find_and_modify.go b/x/mongo/driver/operation/find_and_modify.go index ea365ccb23..89f8874777 100644 --- a/x/mongo/driver/operation/find_and_modify.go +++ b/x/mongo/driver/operation/find_and_modify.go @@ -52,6 +52,7 @@ type FindAndModify struct { serverAPI *driver.ServerAPIOptions let bsoncore.Document timeout *time.Duration + bypassEmptyTsReplacement *bool result FindAndModifyResult } @@ -214,6 +215,9 @@ func (fam *FindAndModify) command(dst []byte, desc description.SelectedServer) ( if fam.let != nil { dst = bsoncore.AppendDocumentElement(dst, "let", fam.let) } + if fam.bypassEmptyTsReplacement != nil { + dst = bsoncore.AppendBooleanElement(dst, "bypassEmptyTsReplacement", *fam.bypassEmptyTsReplacement) + } return dst, nil } @@ -489,3 +493,13 @@ func (fam *FindAndModify) Authenticator(authenticator driver.Authenticator) *Fin fam.authenticator = authenticator return fam } + +// BypassEmptyTsReplacement sets the bypassEmptyTsReplacement to use for this operation. +func (fam *FindAndModify) BypassEmptyTsReplacement(bypassEmptyTsReplacement bool) *FindAndModify { + if fam == nil { + fam = new(FindAndModify) + } + + fam.bypassEmptyTsReplacement = &bypassEmptyTsReplacement + return fam +} diff --git a/x/mongo/driver/operation/insert.go b/x/mongo/driver/operation/insert.go index a65a4895f0..eea285b405 100644 --- a/x/mongo/driver/operation/insert.go +++ b/x/mongo/driver/operation/insert.go @@ -43,6 +43,7 @@ type Insert struct { result InsertResult serverAPI *driver.ServerAPIOptions timeout *time.Duration + bypassEmptyTsReplacement *bool logger *logger.Logger } @@ -131,6 +132,9 @@ func (i *Insert) command(dst []byte, desc description.SelectedServer) ([]byte, e if i.ordered != nil { dst = bsoncore.AppendBooleanElement(dst, "ordered", *i.ordered) } + if i.bypassEmptyTsReplacement != nil { + dst = bsoncore.AppendBooleanElement(dst, "bypassEmptyTsReplacement", *i.bypassEmptyTsReplacement) + } return dst, nil } @@ -317,3 +321,13 @@ func (i *Insert) Authenticator(authenticator driver.Authenticator) *Insert { i.authenticator = authenticator return i } + +// BypassEmptyTsReplacement sets the bypassEmptyTsReplacement to use for this operation. +func (i *Insert) BypassEmptyTsReplacement(bypassEmptyTsReplacement bool) *Insert { + if i == nil { + i = new(Insert) + } + + i.bypassEmptyTsReplacement = &bypassEmptyTsReplacement + return i +} diff --git a/x/mongo/driver/operation/update.go b/x/mongo/driver/operation/update.go index 1070e7ca70..a33c67ee23 100644 --- a/x/mongo/driver/operation/update.go +++ b/x/mongo/driver/operation/update.go @@ -47,6 +47,7 @@ type Update struct { serverAPI *driver.ServerAPIOptions let bsoncore.Document timeout *time.Duration + bypassEmptyTsReplacement *bool logger *logger.Logger } @@ -204,6 +205,9 @@ func (u *Update) command(dst []byte, desc description.SelectedServer) ([]byte, e if u.let != nil { dst = bsoncore.AppendDocumentElement(dst, "let", u.let) } + if u.bypassEmptyTsReplacement != nil { + dst = bsoncore.AppendBooleanElement(dst, "bypassEmptyTsReplacement", *u.bypassEmptyTsReplacement) + } return dst, nil } @@ -426,3 +430,13 @@ func (u *Update) Authenticator(authenticator driver.Authenticator) *Update { u.authenticator = authenticator return u } + +// BypassEmptyTsReplacement sets the bypassEmptyTsReplacement to use for this operation. +func (u *Update) BypassEmptyTsReplacement(bypassEmptyTsReplacement bool) *Update { + if u == nil { + u = new(Update) + } + + u.bypassEmptyTsReplacement = &bypassEmptyTsReplacement + return u +}