Skip to content

Commit 0dfcbab

Browse files
committed
Ensure write operation error are returned
GODRIVER-312 GODRIVER-307 GODRIVER-347 Change-Id: Ib3321215285029b9602f27ca4a89d588ef643238
1 parent 550660b commit 0dfcbab

File tree

5 files changed

+657
-20
lines changed

5 files changed

+657
-20
lines changed

core/integration/command_test.go

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,16 @@ import (
1313
"github.com/mongodb/mongo-go-driver/bson"
1414
"github.com/mongodb/mongo-go-driver/core/addr"
1515
"github.com/mongodb/mongo-go-driver/core/command"
16+
"github.com/mongodb/mongo-go-driver/core/description"
17+
"github.com/mongodb/mongo-go-driver/core/result"
1618
"github.com/mongodb/mongo-go-driver/core/topology"
19+
"github.com/mongodb/mongo-go-driver/internal/testutil"
1720
"github.com/stretchr/testify/require"
1821
)
1922

2023
func TestCommand(t *testing.T) {
2124
noerr := func(t *testing.T, err error) {
22-
// t.Helper()
25+
t.Helper()
2326
if err != nil {
2427
t.Errorf("Unepexted error: %v", err)
2528
t.FailNow()
@@ -74,3 +77,31 @@ func TestCommand(t *testing.T) {
7477
require.Equal(t, elem.Value().Type(), bson.TypeDouble)
7578
require.Equal(t, float64(1), elem.Value().Double(), "Unable to ping MongoDB")
7679
}
80+
81+
func TestWriteCommands(t *testing.T) {
82+
t.Run("Insert", func(t *testing.T) {
83+
t.Run("Should return write error", func(t *testing.T) {
84+
ctx := context.TODO()
85+
server, err := testutil.Topology(t).SelectServer(context.Background(), description.WriteSelector())
86+
noerr(t, err)
87+
conn, err := server.Connection(context.Background())
88+
noerr(t, err)
89+
cmd := &command.Insert{
90+
NS: command.Namespace{DB: dbName, Collection: testutil.ColName(t)},
91+
Docs: []*bson.Document{bson.NewDocument(bson.EC.String("_id", "helloworld"))},
92+
}
93+
_, err = cmd.RoundTrip(ctx, server.SelectedDescription(), conn)
94+
noerr(t, err)
95+
res, err := cmd.RoundTrip(ctx, server.SelectedDescription(), conn)
96+
noerr(t, err)
97+
if len(res.WriteErrors) != 1 {
98+
t.Errorf("Expected to get a write error. got %d; want %d", len(res.WriteErrors), 1)
99+
t.FailNow()
100+
}
101+
want := result.WriteError{Code: 11000}
102+
if res.WriteErrors[0].Code != want.Code {
103+
t.Errorf("Expected error codes to match. want %d; got %d", res.WriteErrors[0].Code, want.Code)
104+
}
105+
})
106+
})
107+
}

core/result/result.go

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,27 @@ import (
1616

1717
// Insert is a result from an Insert command.
1818
type Insert struct {
19-
N int
19+
N int
20+
WriteErrors []WriteError `bson:"writeErrors"`
21+
WriteConcernError *WriteConcernError `bson:"writeConcernError"`
2022
}
2123

2224
// Delete is a result from a Delete command.
2325
type Delete struct {
24-
N int
26+
N int
27+
WriteErrors []WriteError `bson:"writeErrors"`
28+
WriteConcernError *WriteConcernError `bson:"writeConcernError"`
29+
}
30+
31+
// Update is a result of an Update command.
32+
type Update struct {
33+
MatchedCount int64 `bson:"n"`
34+
ModifiedCount int64 `bson:"nModified"`
35+
Upserted []struct {
36+
ID interface{} `bson:"_id"`
37+
} `bson:"upserted"`
38+
WriteErrors []WriteError `bson:"writeErrors"`
39+
WriteConcernError *WriteConcernError `bson:"writeConcernError"`
2540
}
2641

2742
// Distinct is a result from a Distinct command.
@@ -38,8 +53,20 @@ type FindAndModify struct {
3853
}
3954
}
4055

41-
// Document is a result from a command that returns a single Document.
42-
type Document struct{}
56+
// WriteError is an error from a write operation that is not a write concern
57+
// error.
58+
type WriteError struct {
59+
Index int
60+
Code int
61+
ErrMsg string
62+
}
63+
64+
// WriteConcernError is an error related to a write concern.
65+
type WriteConcernError struct {
66+
Code int
67+
ErrMsg string
68+
ErrInfo bson.Reader
69+
}
4370

4471
// ListDatabases is the result from a listDatabases command.
4572
type ListDatabases struct {
@@ -51,15 +78,6 @@ type ListDatabases struct {
5178
TotalSize int64 `bson:"totalSize"`
5279
}
5380

54-
// Update is a result of an Update command.
55-
type Update struct {
56-
MatchedCount int64 `bson:"n"`
57-
ModifiedCount int64 `bson:"nModified"`
58-
Upserted []struct {
59-
ID interface{} `bson:"_id"`
60-
} `bson:"upserted"`
61-
}
62-
6381
// IsMaster is a result of an IsMaster command.
6482
type IsMaster struct {
6583
Arbiters []string `bson:"arbiters,omitempty"`

mongo/collection.go

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,9 @@ func (coll *Collection) InsertOne(ctx context.Context, document interface{},
9696
Opts: newOptions,
9797
}
9898

99-
_, err = dispatch.Insert(ctx, cmd, coll.client.topology, coll.writeSelector, coll.writeConcern)
100-
if err != nil && err != dispatch.ErrUnacknowledgedWrite {
99+
res, err := dispatch.Insert(ctx, cmd, coll.client.topology, coll.writeSelector, coll.writeConcern)
100+
rr, err := processWriteError(res.WriteConcernError, res.WriteErrors, err)
101+
if rr&rrOne == 0 {
101102
return nil, err
102103
}
103104
return &InsertOneResult{InsertedID: insertedID}, err
@@ -149,10 +150,20 @@ func (coll *Collection) InsertMany(ctx context.Context, documents []interface{},
149150
Opts: newOptions,
150151
}
151152

152-
_, err := dispatch.Insert(ctx, cmd, coll.client.topology, coll.writeSelector, coll.writeConcern)
153-
if err != nil && err != dispatch.ErrUnacknowledgedWrite {
153+
res, err := dispatch.Insert(ctx, cmd, coll.client.topology, coll.writeSelector, coll.writeConcern)
154+
switch err {
155+
case nil:
156+
case dispatch.ErrUnacknowledgedWrite:
157+
return &InsertManyResult{InsertedIDs: result}, ErrUnacknowledgedWrite
158+
default:
154159
return nil, err
155160
}
161+
if len(res.WriteErrors) > 0 || res.WriteConcernError != nil {
162+
err = BulkWriteError{
163+
WriteErrors: writeErrorsFromResult(res.WriteErrors),
164+
WriteConcernError: convertWriteConcernError(res.WriteConcernError),
165+
}
166+
}
156167
return &InsertManyResult{InsertedIDs: result}, err
157168
}
158169

@@ -187,7 +198,8 @@ func (coll *Collection) DeleteOne(ctx context.Context, filter interface{},
187198
}
188199

189200
res, err := dispatch.Delete(ctx, cmd, coll.client.topology, coll.writeSelector, coll.writeConcern)
190-
if err != nil && err != dispatch.ErrUnacknowledgedWrite {
201+
rr, err := processWriteError(res.WriteConcernError, res.WriteErrors, err)
202+
if rr&rrOne == 0 {
191203
return nil, err
192204
}
193205
return &DeleteResult{DeletedCount: int64(res.N)}, err
@@ -221,7 +233,8 @@ func (coll *Collection) DeleteMany(ctx context.Context, filter interface{},
221233
}
222234

223235
res, err := dispatch.Delete(ctx, cmd, coll.client.topology, coll.writeSelector, coll.writeConcern)
224-
if err != nil && err != dispatch.ErrUnacknowledgedWrite {
236+
rr, err := processWriteError(res.WriteConcernError, res.WriteErrors, err)
237+
if rr&rrMany == 0 {
225238
return nil, err
226239
}
227240
return &DeleteResult{DeletedCount: int64(res.N)}, err
@@ -262,6 +275,10 @@ func (coll *Collection) updateOrReplaceOne(ctx context.Context, filter,
262275
res.MatchedCount--
263276
}
264277

278+
rr, err := processWriteError(r.WriteConcernError, r.WriteErrors, err)
279+
if rr&rrOne == 0 {
280+
return nil, err
281+
}
265282
return res, err
266283
}
267284

@@ -351,6 +368,10 @@ func (coll *Collection) UpdateMany(ctx context.Context, filter interface{}, upda
351368
res.MatchedCount--
352369
}
353370

371+
rr, err := processWriteError(r.WriteConcernError, r.WriteErrors, err)
372+
if rr&rrMany == 0 {
373+
return nil, err
374+
}
354375
return res, err
355376
}
356377

0 commit comments

Comments
 (0)