blob: 3505bccf2464c323e37ca06c7639d0afc7972f33 [file] [log] [blame]
Don Newton379ae252019-04-01 12:17:06 -04001// 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
7package mongo
8
9import (
10 "bytes"
11 "errors"
12 "fmt"
13
14 "github.com/mongodb/mongo-go-driver/bson"
15 "github.com/mongodb/mongo-go-driver/x/mongo/driver"
16 "github.com/mongodb/mongo-go-driver/x/mongo/driver/topology"
17 "github.com/mongodb/mongo-go-driver/x/network/command"
18 "github.com/mongodb/mongo-go-driver/x/network/result"
19)
20
21// ErrUnacknowledgedWrite is returned from functions that have an unacknowledged
22// write concern.
23var ErrUnacknowledgedWrite = errors.New("unacknowledged write")
24
25// ErrClientDisconnected is returned when a user attempts to call a method on a
26// disconnected client
27var ErrClientDisconnected = errors.New("client is disconnected")
28
29// ErrNilDocument is returned when a user attempts to pass a nil document or filter
30// to a function where the field is required.
31var ErrNilDocument = errors.New("document is nil")
32
33// ErrEmptySlice is returned when a user attempts to pass an empty slice as input
34// to a function wehere the field is required.
35var ErrEmptySlice = errors.New("must provide at least one element in input slice")
36
37func replaceTopologyErr(err error) error {
38 if err == topology.ErrTopologyClosed {
39 return ErrClientDisconnected
40 }
41 return err
42}
43
44// WriteError is a non-write concern failure that occurred as a result of a write
45// operation.
46type WriteError struct {
47 Index int
48 Code int
49 Message string
50}
51
52func (we WriteError) Error() string { return we.Message }
53
54// WriteErrors is a group of non-write concern failures that occurred as a result
55// of a write operation.
56type WriteErrors []WriteError
57
58func (we WriteErrors) Error() string {
59 var buf bytes.Buffer
60 fmt.Fprint(&buf, "write errors: [")
61 for idx, err := range we {
62 if idx != 0 {
63 fmt.Fprintf(&buf, ", ")
64 }
65 fmt.Fprintf(&buf, "{%s}", err)
66 }
67 fmt.Fprint(&buf, "]")
68 return buf.String()
69}
70
71func writeErrorsFromResult(rwes []result.WriteError) WriteErrors {
72 wes := make(WriteErrors, 0, len(rwes))
73 for _, err := range rwes {
74 wes = append(wes, WriteError{Index: err.Index, Code: err.Code, Message: err.ErrMsg})
75 }
76 return wes
77}
78
79// WriteConcernError is a write concern failure that occurred as a result of a
80// write operation.
81type WriteConcernError struct {
82 Code int
83 Message string
84 Details bson.Raw
85}
86
87func (wce WriteConcernError) Error() string { return wce.Message }
88
89// WriteException is an error for a non-bulk write operation.
90type WriteException struct {
91 WriteConcernError *WriteConcernError
92 WriteErrors WriteErrors
93}
94
95func (mwe WriteException) Error() string {
96 var buf bytes.Buffer
97 fmt.Fprint(&buf, "multiple write errors: [")
98 fmt.Fprintf(&buf, "{%s}, ", mwe.WriteErrors)
99 fmt.Fprintf(&buf, "{%s}]", mwe.WriteConcernError)
100 return buf.String()
101}
102
103func convertBulkWriteErrors(errors []driver.BulkWriteError) []BulkWriteError {
104 bwErrors := make([]BulkWriteError, 0, len(errors))
105 for _, err := range errors {
106 bwErrors = append(bwErrors, BulkWriteError{
107 WriteError{
108 Index: err.Index,
109 Code: err.Code,
110 Message: err.ErrMsg,
111 },
112 dispatchToMongoModel(err.Model),
113 })
114 }
115
116 return bwErrors
117}
118
119func convertWriteConcernError(wce *result.WriteConcernError) *WriteConcernError {
120 if wce == nil {
121 return nil
122 }
123
124 return &WriteConcernError{Code: wce.Code, Message: wce.ErrMsg, Details: wce.ErrInfo}
125}
126
127// BulkWriteError is an error for one operation in a bulk write.
128type BulkWriteError struct {
129 WriteError
130 Request WriteModel
131}
132
133func (bwe BulkWriteError) Error() string {
134 var buf bytes.Buffer
135 fmt.Fprintf(&buf, "{%s}", bwe.WriteError)
136 return buf.String()
137}
138
139// BulkWriteException is an error for a bulk write operation.
140type BulkWriteException struct {
141 WriteConcernError *WriteConcernError
142 WriteErrors []BulkWriteError
143}
144
145func (bwe BulkWriteException) Error() string {
146 var buf bytes.Buffer
147 fmt.Fprint(&buf, "bulk write error: [")
148 fmt.Fprintf(&buf, "{%s}, ", bwe.WriteErrors)
149 fmt.Fprintf(&buf, "{%s}]", bwe.WriteConcernError)
150 return buf.String()
151}
152
153// returnResult is used to determine if a function calling processWriteError should return
154// the result or return nil. Since the processWriteError function is used by many different
155// methods, both *One and *Many, we need a way to differentiate if the method should return
156// the result and the error.
157type returnResult int
158
159const (
160 rrNone returnResult = 1 << iota // None means do not return the result ever.
161 rrOne // One means return the result if this was called by a *One method.
162 rrMany // Many means return the result is this was called by a *Many method.
163
164 rrAll returnResult = rrOne | rrMany // All means always return the result.
165)
166
167// processWriteError handles processing the result of a write operation. If the retrunResult matches
168// the calling method's type, it should return the result object in addition to the error.
169// This function will wrap the errors from other packages and return them as errors from this package.
170//
171// WriteConcernError will be returned over WriteErrors if both are present.
172func processWriteError(wce *result.WriteConcernError, wes []result.WriteError, err error) (returnResult, error) {
173 switch {
174 case err == command.ErrUnacknowledgedWrite:
175 return rrAll, ErrUnacknowledgedWrite
176 case err != nil:
177 return rrNone, replaceTopologyErr(err)
178 case wce != nil || len(wes) > 0:
179 return rrMany, WriteException{
180 WriteConcernError: convertWriteConcernError(wce),
181 WriteErrors: writeErrorsFromResult(wes),
182 }
183 default:
184 return rrAll, nil
185 }
186}