@@ -8,6 +8,7 @@ package mongo
88
99import  (
1010	"context" 
11+ 	"errors" 
1112	"strconv" 
1213
1314	"go.mongodb.org/mongo-driver/bson" 
@@ -40,6 +41,9 @@ type clientBulkWrite struct {
4041}
4142
4243func  (bw  * clientBulkWrite ) execute (ctx  context.Context ) error  {
44+ 	if  len (bw .models ) ==  0  {
45+ 		return  errors .New ("empty write models" )
46+ 	}
4347	docs  :=  make ([]bsoncore.Document , len (bw .models ))
4448	nsMap  :=  make (map [string ]int )
4549	var  nsList  []string 
@@ -170,12 +174,21 @@ func (bw *clientBulkWrite) execute(ctx context.Context) error {
170174		Database ("admin" ).
171175		Deployment (bw .client .deployment ).Crypt (bw .client .cryptFLE ).
172176		ServerAPI (bw .client .serverAPI ).Timeout (bw .client .timeout ).
173- 		Logger (bw .client .logger ).Authenticator (bw .client .authenticator )
174- 	err  :=  op .Execute (ctx )
175- 	if  err  !=  nil  {
176- 		return  err 
177+ 		Logger (bw .client .logger ).Authenticator (bw .client .authenticator ).Name ("bulkWrite" )
178+ 	opErr  :=  op .Execute (ctx )
179+ 	var  wcErrs  []* WriteConcernError 
180+ 	if  opErr  !=  nil  {
181+ 		if  errors .Is (opErr , driver .ErrUnacknowledgedWrite ) {
182+ 			return  nil 
183+ 		}
184+ 		var  writeErr  driver.WriteCommandError 
185+ 		if  errors .As (opErr , & writeErr ) {
186+ 			wcErr  :=  convertDriverWriteConcernError (writeErr .WriteConcernError )
187+ 			wcErrs  =  append (wcErrs , wcErr )
188+ 		}
177189	}
178190	var  res  struct  {
191+ 		Ok      bool 
179192		Cursor  struct  {
180193			FirstBatch  []bson.Raw 
181194		}
@@ -184,33 +197,48 @@ func (bw *clientBulkWrite) execute(ctx context.Context) error {
184197		NMatched   int32 
185198		NModified  int32 
186199		NUpserted  int32 
200+ 		NErrors    int32 
201+ 		Code       int32 
202+ 		Errmsg     string 
187203	}
188204	rawRes  :=  op .Result ()
189- 	err  =  bson .Unmarshal (rawRes , & res )
190- 	if  err  !=  nil  {
205+ 	if  err  :=  bson .Unmarshal (rawRes , & res ); err  !=  nil  {
191206		return  err 
192207	}
193208	bw .result .DeletedCount  =  int64 (res .NDeleted )
194209	bw .result .InsertedCount  =  int64 (res .NInserted )
195210	bw .result .MatchedCount  =  int64 (res .NMatched )
196211	bw .result .ModifiedCount  =  int64 (res .NModified )
197212	bw .result .UpsertedCount  =  int64 (res .NUpserted )
213+ 	errors  :=  make (map [int64 ]WriteError )
198214	for  i , cur  :=  range  res .Cursor .FirstBatch  {
199215		switch  res  :=  resMap [i ].(type ) {
200216		case  map [int64 ]ClientDeleteResult :
201- 			if  err  =  appendDeleteResult (cur , res ); err  !=  nil  {
217+ 			if  err  : =  appendDeleteResult (cur , res ,  errors ); err  !=  nil  {
202218				return  err 
203219			}
204220		case  map [int64 ]ClientInsertResult :
205- 			if  err  =  appendInsertResult (cur , res , insIDMap ); err  !=  nil  {
221+ 			if  err  : =  appendInsertResult (cur , res ,  errors , insIDMap ); err  !=  nil  {
206222				return  err 
207223			}
208224		case  map [int64 ]ClientUpdateResult :
209- 			if  err  =  appendUpdateResult (cur , res ); err  !=  nil  {
225+ 			if  err  : =  appendUpdateResult (cur , res ,  errors ); err  !=  nil  {
210226				return  err 
211227			}
212228		}
213229	}
230+ 	if  ! res .Ok  ||  res .NErrors  >  0  ||  opErr  !=  nil  {
231+ 		return  ClientBulkWriteException {
232+ 			TopLevelError : & WriteError {
233+ 				Code :    int (res .Code ),
234+ 				Message : res .Errmsg ,
235+ 				Raw :     bson .Raw (rawRes ),
236+ 			},
237+ 			WriteConcernErrors : wcErrs ,
238+ 			WriteErrors :        errors ,
239+ 			PartialResult :      & bw .result ,
240+ 		}
241+ 	}
214242	return  nil 
215243}
216244
@@ -383,45 +411,75 @@ func createClientDeleteDoc(
383411	return  bsoncore .AppendDocumentEnd (doc , didx )
384412}
385413
386- func  appendDeleteResult (cur  bson.Raw , m  map [int64 ]ClientDeleteResult ) error  {
414+ func  appendDeleteResult (cur  bson.Raw , m  map [int64 ]ClientDeleteResult ,  e   map [ int64 ] WriteError ) error  {
387415	var  res  struct  {
388- 		Idx  int32 
389- 		N    int32 
416+ 		Ok      bool 
417+ 		Idx     int32 
418+ 		N       int32 
419+ 		Code    int32 
420+ 		Errmsg  string 
390421	}
391422	if  err  :=  bson .Unmarshal (cur , & res ); err  !=  nil  {
392423		return  err 
393424	}
394- 	m [int64 (res .Idx )] =  ClientDeleteResult {int64 (res .N )}
425+ 	if  res .Ok  {
426+ 		m [int64 (res .Idx )] =  ClientDeleteResult {int64 (res .N )}
427+ 	} else  {
428+ 		e [int64 (res .Idx )] =  WriteError {
429+ 			Code :    int (res .Code ),
430+ 			Message : res .Errmsg ,
431+ 		}
432+ 	}
395433	return  nil 
396434}
397435
398- func  appendInsertResult (cur  bson.Raw , m  map [int64 ]ClientInsertResult , insIdMap  map [int ]interface {}) error  {
436+ func  appendInsertResult (cur  bson.Raw , m  map [int64 ]ClientInsertResult , e   map [ int64 ] WriteError ,  insIDMap  map [int ]interface {}) error  {
399437	var  res  struct  {
400- 		Idx  int32 
438+ 		Ok      bool 
439+ 		Idx     int32 
440+ 		Code    int32 
441+ 		Errmsg  string 
401442	}
402443	if  err  :=  bson .Unmarshal (cur , & res ); err  !=  nil  {
403444		return  err 
404445	}
405- 	m [int64 (res .Idx )] =  ClientInsertResult {insIdMap [int (res .Idx )]}
446+ 	if  res .Ok  {
447+ 		m [int64 (res .Idx )] =  ClientInsertResult {insIDMap [int (res .Idx )]}
448+ 	} else  {
449+ 		e [int64 (res .Idx )] =  WriteError {
450+ 			Code :    int (res .Code ),
451+ 			Message : res .Errmsg ,
452+ 		}
453+ 	}
406454	return  nil 
407455}
408456
409- func  appendUpdateResult (cur  bson.Raw , m  map [int64 ]ClientUpdateResult ) error  {
457+ func  appendUpdateResult (cur  bson.Raw , m  map [int64 ]ClientUpdateResult ,  e   map [ int64 ] WriteError ) error  {
410458	var  res  struct  {
459+ 		Ok         bool 
411460		Idx        int32 
412461		N          int32 
413462		NModified  int32 
414463		Upserted   struct  {
415464			ID  interface {} `bson:"_id"` 
416465		}
466+ 		Code    int32 
467+ 		Errmsg  string 
417468	}
418469	if  err  :=  bson .Unmarshal (cur , & res ); err  !=  nil  {
419470		return  err 
420471	}
421- 	m [int64 (res .Idx )] =  ClientUpdateResult {
422- 		MatchedCount :  int64 (res .N ),
423- 		ModifiedCount : int64 (res .NModified ),
424- 		UpsertedID :    res .Upserted .ID ,
472+ 	if  res .Ok  {
473+ 		m [int64 (res .Idx )] =  ClientUpdateResult {
474+ 			MatchedCount :  int64 (res .N ),
475+ 			ModifiedCount : int64 (res .NModified ),
476+ 			UpsertedID :    res .Upserted .ID ,
477+ 		}
478+ 	} else  {
479+ 		e [int64 (res .Idx )] =  WriteError {
480+ 			Code :    int (res .Code ),
481+ 			Message : res .Errmsg ,
482+ 		}
425483	}
426484	return  nil 
427485}
0 commit comments