blob: 5ecb48cce7d878e56b5a12340b10adff943ca172 [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 command
8
9import (
10 "errors"
11 "fmt"
12
13 "strings"
14
15 "github.com/mongodb/mongo-go-driver/bson"
16 "github.com/mongodb/mongo-go-driver/x/network/result"
17)
18
19var (
20 // ErrUnknownCommandFailure occurs when a command fails for an unknown reason.
21 ErrUnknownCommandFailure = errors.New("unknown command failure")
22 // ErrNoCommandResponse occurs when the server sent no response document to a command.
23 ErrNoCommandResponse = errors.New("no command response document")
24 // ErrMultiDocCommandResponse occurs when the server sent multiple documents in response to a command.
25 ErrMultiDocCommandResponse = errors.New("command returned multiple documents")
26 // ErrNoDocCommandResponse occurs when the server indicated a response existed, but none was found.
27 ErrNoDocCommandResponse = errors.New("command returned no documents")
28 // ErrDocumentTooLarge occurs when a document that is larger than the maximum size accepted by a
29 // server is passed to an insert command.
30 ErrDocumentTooLarge = errors.New("an inserted document is too large")
31 // ErrNonPrimaryRP occurs when a nonprimary read preference is used with a transaction.
32 ErrNonPrimaryRP = errors.New("read preference in a transaction must be primary")
33 // UnknownTransactionCommitResult is an error label for unknown transaction commit results.
34 UnknownTransactionCommitResult = "UnknownTransactionCommitResult"
35 // TransientTransactionError is an error label for transient errors with transactions.
36 TransientTransactionError = "TransientTransactionError"
37 // NetworkError is an error label for network errors.
38 NetworkError = "NetworkError"
39 // ReplyDocumentMismatch is an error label for OP_QUERY field mismatch errors.
40 ReplyDocumentMismatch = "malformed OP_REPLY: NumberReturned does not match number of documents returned"
41)
42
43var retryableCodes = []int32{11600, 11602, 10107, 13435, 13436, 189, 91, 7, 6, 89, 9001}
44
45// QueryFailureError is an error representing a command failure as a document.
46type QueryFailureError struct {
47 Message string
48 Response bson.Raw
49}
50
51// Error implements the error interface.
52func (e QueryFailureError) Error() string {
53 return fmt.Sprintf("%s: %v", e.Message, e.Response)
54}
55
56// ResponseError is an error parsing the response to a command.
57type ResponseError struct {
58 Message string
59 Wrapped error
60}
61
62// NewCommandResponseError creates a CommandResponseError.
63func NewCommandResponseError(msg string, err error) ResponseError {
64 return ResponseError{Message: msg, Wrapped: err}
65}
66
67// Error implements the error interface.
68func (e ResponseError) Error() string {
69 if e.Wrapped != nil {
70 return fmt.Sprintf("%s: %s", e.Message, e.Wrapped)
71 }
72 return fmt.Sprintf("%s", e.Message)
73}
74
75// Error is a command execution error from the database.
76type Error struct {
77 Code int32
78 Message string
79 Labels []string
80 Name string
81}
82
83// Error implements the error interface.
84func (e Error) Error() string {
85 if e.Name != "" {
86 return fmt.Sprintf("(%v) %v", e.Name, e.Message)
87 }
88 return e.Message
89}
90
91// HasErrorLabel returns true if the error contains the specified label.
92func (e Error) HasErrorLabel(label string) bool {
93 if e.Labels != nil {
94 for _, l := range e.Labels {
95 if l == label {
96 return true
97 }
98 }
99 }
100 return false
101}
102
103// Retryable returns true if the error is retryable
104func (e Error) Retryable() bool {
105 for _, label := range e.Labels {
106 if label == NetworkError {
107 return true
108 }
109 }
110 for _, code := range retryableCodes {
111 if e.Code == code {
112 return true
113 }
114 }
115 if strings.Contains(e.Message, "not master") || strings.Contains(e.Message, "node is recovering") {
116 return true
117 }
118
119 return false
120}
121
122// IsWriteConcernErrorRetryable returns true if the write concern error is retryable.
123func IsWriteConcernErrorRetryable(wce *result.WriteConcernError) bool {
124 for _, code := range retryableCodes {
125 if int32(wce.Code) == code {
126 return true
127 }
128 }
129 if strings.Contains(wce.ErrMsg, "not master") || strings.Contains(wce.ErrMsg, "node is recovering") {
130 return true
131 }
132
133 return false
134}
135
136// IsNotFound indicates if the error is from a namespace not being found.
137func IsNotFound(err error) bool {
138 e, ok := err.(Error)
139 // need message check because legacy servers don't include the error code
140 return ok && (e.Code == 26 || e.Message == "ns not found")
141}