@@ -42,28 +42,53 @@ class Error
4242 # Class for parsing the various forms that errors can come in from MongoDB
4343 # command responses.
4444 #
45+ # The errors can be reported by the server in a number of ways:
46+ # - {ok:0} response indicates failure. In newer servers, code, codeName
47+ # and errmsg fields should be set. In older servers some may not be set.
48+ # - {ok:1} response with a write concern error (writeConcernError top-level
49+ # field). This indicates that the node responding successfully executed
50+ # the request, but not enough other nodes successfully executed the
51+ # request to satisfy the write concern.
52+ # - {ok:1} response with writeErrors top-level field. This can be obtained
53+ # in a bulk write but also in a non-bulk write. In a non-bulk write
54+ # there should be exactly one error in the writeErrors list.
55+ # The case of multiple errors is handled by BulkWrite::Result.
56+ # - {ok:1} response with writeConcernErrors top-level field. This can
57+ # only be obtained in a bulk write and is handled by BulkWrite::Result,
58+ # not by this class.
59+ #
60+ # Note that writeErrors do not have codeName fields - they just provide
61+ # codes and messages. writeConcernErrors may similarly not provide code
62+ # names.
63+ #
4564 # @since 2.0.0
65+ # @api private
4666 class Parser
4767 include SdamErrorDetection
4868
49- # @return [ BSON::Document ] document The returned document.
69+ # @return [ BSON::Document ] The returned document.
5070 attr_reader :document
5171
52- # @return [ String ] message The error message parsed from the document.
72+ # @return [ String ] The full error message to be used in the
73+ # raised exception.
5374 attr_reader :message
5475
55- # @return [ Array<Protocol::Message> ] replies The message replies.
76+ # @return [ String ] The server-returned error message
77+ # parsed from the response.
78+ attr_reader :server_message
79+
80+ # @return [ Array<Protocol::Message> ] The message replies.
5681 attr_reader :replies
5782
58- # @return [ Integer ] code The error code parsed from the document.
83+ # @return [ Integer ] The error code parsed from the document.
5984 # @since 2.6.0
6085 attr_reader :code
6186
62- # @return [ String ] code_name The error code name parsed from the document.
87+ # @return [ String ] The error code name parsed from the document.
6388 # @since 2.6.0
6489 attr_reader :code_name
6590
66- # @return [ Array<String> ] labels The set of labels associated with the error.
91+ # @return [ Array<String> ] The set of labels associated with the error.
6792 # @since 2.7.0
6893 attr_reader :labels
6994
@@ -145,33 +170,61 @@ def write_concern_error_labels
145170 write_concern_error_document && write_concern_error_document [ 'errorLabels' ]
146171 end
147172
173+ class << self
174+ def build_message ( code : nil , code_name : nil , message : nil )
175+ if code_name && code
176+ "[#{ code } :#{ code_name } ]: #{ message } "
177+ elsif code_name
178+ # This surely should never happen, if there's a code name
179+ # there ought to also be the code provided.
180+ # Handle this case for completeness.
181+ "[#{ code_name } ]: #{ message } "
182+ elsif code
183+ "[#{ code } ]: #{ message } "
184+ else
185+ message
186+ end
187+ end
188+ end
189+
148190 private
149191
150192 def parse!
193+ if document [ 'ok' ] != 1 && document [ 'writeErrors' ]
194+ raise ArgumentError , "writeErrors should only be given in successful responses"
195+ end
196+
151197 @message = ""
152- parse_single ( @message , ERR )
153- parse_single ( @message , ERROR )
154- parse_single ( @message , ERRMSG )
198+ parse_single ( @message , '$err' )
199+ parse_single ( @message , 'err' )
200+ parse_single ( @message , 'errmsg' )
155201 parse_multiple ( @message , 'writeErrors' )
156202 if write_concern_error_document
157- parse_single ( @message , ERRMSG , write_concern_error_document )
203+ parse_single ( @message , 'errmsg' , write_concern_error_document )
158204 end
159205 parse_flag ( @message )
160206 parse_code
161207 parse_labels
162208 parse_wtimeout
209+
210+ @server_message = @message
211+ @message = self . class . build_message (
212+ code : code ,
213+ code_name : code_name ,
214+ message : @message ,
215+ )
163216 end
164217
165218 def parse_single ( message , key , doc = document )
166219 if error = doc [ key ]
167- append ( message , " #{ error } ( #{ doc [ CODE ] } )" )
220+ append ( message , error )
168221 end
169222 end
170223
171224 def parse_multiple ( message , key )
172225 if errors = document [ key ]
173226 errors . each do |error |
174- parse_single ( message , ERRMSG , error )
227+ parse_single ( message , 'errmsg' , error )
175228 end
176229 end
177230 end
0 commit comments