github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/vendor_skip/go.mongodb.org/mongo-driver/x/mongo/driver/errors.go (about)

     1  // Copyright (C) MongoDB, Inc. 2022-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  
     7  package driver
     8  
     9  import (
    10  	"bytes"
    11  	"errors"
    12  	"fmt"
    13  	"strings"
    14  
    15  	"go.mongodb.org/mongo-driver/bson"
    16  	"go.mongodb.org/mongo-driver/internal"
    17  	"go.mongodb.org/mongo-driver/mongo/description"
    18  	"go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
    19  )
    20  
    21  var (
    22  	retryableCodes          = []int32{11600, 11602, 10107, 13435, 13436, 189, 91, 7, 6, 89, 9001, 262}
    23  	nodeIsRecoveringCodes   = []int32{11600, 11602, 13436, 189, 91}
    24  	notPrimaryCodes         = []int32{10107, 13435, 10058}
    25  	nodeIsShuttingDownCodes = []int32{11600, 91}
    26  
    27  	unknownReplWriteConcernCode   = int32(79)
    28  	unsatisfiableWriteConcernCode = int32(100)
    29  )
    30  
    31  var (
    32  	// UnknownTransactionCommitResult is an error label for unknown transaction commit results.
    33  	UnknownTransactionCommitResult = "UnknownTransactionCommitResult"
    34  	// TransientTransactionError is an error label for transient errors with transactions.
    35  	TransientTransactionError = "TransientTransactionError"
    36  	// NetworkError is an error label for network errors.
    37  	NetworkError = "NetworkError"
    38  	// RetryableWriteError is an error lable for retryable write errors.
    39  	RetryableWriteError = "RetryableWriteError"
    40  	// NoWritesPerformed is an error label indicated that no writes were performed for an operation.
    41  	NoWritesPerformed = "NoWritesPerformed"
    42  	// ErrCursorNotFound is the cursor not found error for legacy find operations.
    43  	ErrCursorNotFound = errors.New("cursor not found")
    44  	// ErrUnacknowledgedWrite is returned from functions that have an unacknowledged
    45  	// write concern.
    46  	ErrUnacknowledgedWrite = errors.New("unacknowledged write")
    47  	// ErrUnsupportedStorageEngine is returned when a retryable write is attempted against a server
    48  	// that uses a storage engine that does not support retryable writes
    49  	ErrUnsupportedStorageEngine = errors.New("this MongoDB deployment does not support retryable writes. Please add retryWrites=false to your connection string")
    50  	// ErrDeadlineWouldBeExceeded is returned when a Timeout set on an operation would be exceeded
    51  	// if the operation were sent to the server.
    52  	ErrDeadlineWouldBeExceeded = errors.New("operation not sent to server, as Timeout would be exceeded")
    53  	// ErrNegativeMaxTime is returned when MaxTime on an operation is a negative value.
    54  	ErrNegativeMaxTime = errors.New("a negative value was provided for MaxTime on an operation")
    55  )
    56  
    57  // QueryFailureError is an error representing a command failure as a document.
    58  type QueryFailureError struct {
    59  	Message  string
    60  	Response bsoncore.Document
    61  	Wrapped  error
    62  }
    63  
    64  // Error implements the error interface.
    65  func (e QueryFailureError) Error() string {
    66  	return fmt.Sprintf("%s: %v", e.Message, e.Response)
    67  }
    68  
    69  // Unwrap returns the underlying error.
    70  func (e QueryFailureError) Unwrap() error {
    71  	return e.Wrapped
    72  }
    73  
    74  // ResponseError is an error parsing the response to a command.
    75  type ResponseError struct {
    76  	Message string
    77  	Wrapped error
    78  }
    79  
    80  // NewCommandResponseError creates a CommandResponseError.
    81  func NewCommandResponseError(msg string, err error) ResponseError {
    82  	return ResponseError{Message: msg, Wrapped: err}
    83  }
    84  
    85  // Error implements the error interface.
    86  func (e ResponseError) Error() string {
    87  	if e.Wrapped != nil {
    88  		return fmt.Sprintf("%s: %s", e.Message, e.Wrapped)
    89  	}
    90  	return e.Message
    91  }
    92  
    93  // WriteCommandError is an error for a write command.
    94  type WriteCommandError struct {
    95  	WriteConcernError *WriteConcernError
    96  	WriteErrors       WriteErrors
    97  	Labels            []string
    98  	Raw               bsoncore.Document
    99  }
   100  
   101  // UnsupportedStorageEngine returns whether or not the WriteCommandError comes from a retryable write being attempted
   102  // against a server that has a storage engine where they are not supported
   103  func (wce WriteCommandError) UnsupportedStorageEngine() bool {
   104  	for _, writeError := range wce.WriteErrors {
   105  		if writeError.Code == 20 && strings.HasPrefix(strings.ToLower(writeError.Message), "transaction numbers") {
   106  			return true
   107  		}
   108  	}
   109  	return false
   110  }
   111  
   112  func (wce WriteCommandError) Error() string {
   113  	var buf bytes.Buffer
   114  	fmt.Fprint(&buf, "write command error: [")
   115  	fmt.Fprintf(&buf, "{%s}, ", wce.WriteErrors)
   116  	fmt.Fprintf(&buf, "{%s}]", wce.WriteConcernError)
   117  	return buf.String()
   118  }
   119  
   120  // Retryable returns true if the error is retryable
   121  func (wce WriteCommandError) Retryable(wireVersion *description.VersionRange) bool {
   122  	for _, label := range wce.Labels {
   123  		if label == RetryableWriteError {
   124  			return true
   125  		}
   126  	}
   127  	if wireVersion != nil && wireVersion.Max >= 9 {
   128  		return false
   129  	}
   130  
   131  	if wce.WriteConcernError == nil {
   132  		return false
   133  	}
   134  	return (*wce.WriteConcernError).Retryable()
   135  }
   136  
   137  // HasErrorLabel returns true if the error contains the specified label.
   138  func (wce WriteCommandError) HasErrorLabel(label string) bool {
   139  	if wce.Labels != nil {
   140  		for _, l := range wce.Labels {
   141  			if l == label {
   142  				return true
   143  			}
   144  		}
   145  	}
   146  	return false
   147  }
   148  
   149  // WriteConcernError is a write concern failure that occurred as a result of a
   150  // write operation.
   151  type WriteConcernError struct {
   152  	Name            string
   153  	Code            int64
   154  	Message         string
   155  	Details         bsoncore.Document
   156  	Labels          []string
   157  	TopologyVersion *description.TopologyVersion
   158  	Raw             bsoncore.Document
   159  }
   160  
   161  func (wce WriteConcernError) Error() string {
   162  	if wce.Name != "" {
   163  		return fmt.Sprintf("(%v) %v", wce.Name, wce.Message)
   164  	}
   165  	return wce.Message
   166  }
   167  
   168  // Retryable returns true if the error is retryable
   169  func (wce WriteConcernError) Retryable() bool {
   170  	for _, code := range retryableCodes {
   171  		if wce.Code == int64(code) {
   172  			return true
   173  		}
   174  	}
   175  
   176  	return false
   177  }
   178  
   179  // NodeIsRecovering returns true if this error is a node is recovering error.
   180  func (wce WriteConcernError) NodeIsRecovering() bool {
   181  	for _, code := range nodeIsRecoveringCodes {
   182  		if wce.Code == int64(code) {
   183  			return true
   184  		}
   185  	}
   186  	hasNoCode := wce.Code == 0
   187  	return hasNoCode && strings.Contains(wce.Message, "node is recovering")
   188  }
   189  
   190  // NodeIsShuttingDown returns true if this error is a node is shutting down error.
   191  func (wce WriteConcernError) NodeIsShuttingDown() bool {
   192  	for _, code := range nodeIsShuttingDownCodes {
   193  		if wce.Code == int64(code) {
   194  			return true
   195  		}
   196  	}
   197  	hasNoCode := wce.Code == 0
   198  	return hasNoCode && strings.Contains(wce.Message, "node is shutting down")
   199  }
   200  
   201  // NotPrimary returns true if this error is a not primary error.
   202  func (wce WriteConcernError) NotPrimary() bool {
   203  	for _, code := range notPrimaryCodes {
   204  		if wce.Code == int64(code) {
   205  			return true
   206  		}
   207  	}
   208  	hasNoCode := wce.Code == 0
   209  	return hasNoCode && strings.Contains(wce.Message, internal.LegacyNotPrimary)
   210  }
   211  
   212  // WriteError is a non-write concern failure that occurred as a result of a write
   213  // operation.
   214  type WriteError struct {
   215  	Index   int64
   216  	Code    int64
   217  	Message string
   218  	Details bsoncore.Document
   219  	Raw     bsoncore.Document
   220  }
   221  
   222  func (we WriteError) Error() string { return we.Message }
   223  
   224  // WriteErrors is a group of non-write concern failures that occurred as a result
   225  // of a write operation.
   226  type WriteErrors []WriteError
   227  
   228  func (we WriteErrors) Error() string {
   229  	var buf bytes.Buffer
   230  	fmt.Fprint(&buf, "write errors: [")
   231  	for idx, err := range we {
   232  		if idx != 0 {
   233  			fmt.Fprintf(&buf, ", ")
   234  		}
   235  		fmt.Fprintf(&buf, "{%s}", err)
   236  	}
   237  	fmt.Fprint(&buf, "]")
   238  	return buf.String()
   239  }
   240  
   241  // Error is a command execution error from the database.
   242  type Error struct {
   243  	Code            int32
   244  	Message         string
   245  	Labels          []string
   246  	Name            string
   247  	Wrapped         error
   248  	TopologyVersion *description.TopologyVersion
   249  	Raw             bsoncore.Document
   250  }
   251  
   252  // UnsupportedStorageEngine returns whether e came as a result of an unsupported storage engine
   253  func (e Error) UnsupportedStorageEngine() bool {
   254  	return e.Code == 20 && strings.HasPrefix(strings.ToLower(e.Message), "transaction numbers")
   255  }
   256  
   257  // Error implements the error interface.
   258  func (e Error) Error() string {
   259  	if e.Name != "" {
   260  		return fmt.Sprintf("(%v) %v", e.Name, e.Message)
   261  	}
   262  	return e.Message
   263  }
   264  
   265  // Unwrap returns the underlying error.
   266  func (e Error) Unwrap() error {
   267  	return e.Wrapped
   268  }
   269  
   270  // HasErrorLabel returns true if the error contains the specified label.
   271  func (e Error) HasErrorLabel(label string) bool {
   272  	if e.Labels != nil {
   273  		for _, l := range e.Labels {
   274  			if l == label {
   275  				return true
   276  			}
   277  		}
   278  	}
   279  	return false
   280  }
   281  
   282  // RetryableRead returns true if the error is retryable for a read operation
   283  func (e Error) RetryableRead() bool {
   284  	for _, label := range e.Labels {
   285  		if label == NetworkError {
   286  			return true
   287  		}
   288  	}
   289  	for _, code := range retryableCodes {
   290  		if e.Code == code {
   291  			return true
   292  		}
   293  	}
   294  
   295  	return false
   296  }
   297  
   298  // RetryableWrite returns true if the error is retryable for a write operation
   299  func (e Error) RetryableWrite(wireVersion *description.VersionRange) bool {
   300  	for _, label := range e.Labels {
   301  		if label == NetworkError || label == RetryableWriteError {
   302  			return true
   303  		}
   304  	}
   305  	if wireVersion != nil && wireVersion.Max >= 9 {
   306  		return false
   307  	}
   308  	for _, code := range retryableCodes {
   309  		if e.Code == code {
   310  			return true
   311  		}
   312  	}
   313  
   314  	return false
   315  }
   316  
   317  // NetworkError returns true if the error is a network error.
   318  func (e Error) NetworkError() bool {
   319  	for _, label := range e.Labels {
   320  		if label == NetworkError {
   321  			return true
   322  		}
   323  	}
   324  	return false
   325  }
   326  
   327  // NodeIsRecovering returns true if this error is a node is recovering error.
   328  func (e Error) NodeIsRecovering() bool {
   329  	for _, code := range nodeIsRecoveringCodes {
   330  		if e.Code == code {
   331  			return true
   332  		}
   333  	}
   334  	hasNoCode := e.Code == 0
   335  	return hasNoCode && strings.Contains(e.Message, "node is recovering")
   336  }
   337  
   338  // NodeIsShuttingDown returns true if this error is a node is shutting down error.
   339  func (e Error) NodeIsShuttingDown() bool {
   340  	for _, code := range nodeIsShuttingDownCodes {
   341  		if e.Code == code {
   342  			return true
   343  		}
   344  	}
   345  	hasNoCode := e.Code == 0
   346  	return hasNoCode && strings.Contains(e.Message, "node is shutting down")
   347  }
   348  
   349  // NotPrimary returns true if this error is a not primary error.
   350  func (e Error) NotPrimary() bool {
   351  	for _, code := range notPrimaryCodes {
   352  		if e.Code == code {
   353  			return true
   354  		}
   355  	}
   356  	hasNoCode := e.Code == 0
   357  	return hasNoCode && strings.Contains(e.Message, internal.LegacyNotPrimary)
   358  }
   359  
   360  // NamespaceNotFound returns true if this errors is a NamespaceNotFound error.
   361  func (e Error) NamespaceNotFound() bool {
   362  	return e.Code == 26 || e.Message == "ns not found"
   363  }
   364  
   365  // ExtractErrorFromServerResponse extracts an error from a server response bsoncore.Document
   366  // if there is one. Also used in testing for SDAM.
   367  func ExtractErrorFromServerResponse(doc bsoncore.Document) error {
   368  	var errmsg, codeName string
   369  	var code int32
   370  	var labels []string
   371  	var ok bool
   372  	var tv *description.TopologyVersion
   373  	var wcError WriteCommandError
   374  	elems, err := doc.Elements()
   375  	if err != nil {
   376  		return err
   377  	}
   378  
   379  	for _, elem := range elems {
   380  		switch elem.Key() {
   381  		case "ok":
   382  			switch elem.Value().Type {
   383  			case bson.TypeInt32:
   384  				if elem.Value().Int32() == 1 {
   385  					ok = true
   386  				}
   387  			case bson.TypeInt64:
   388  				if elem.Value().Int64() == 1 {
   389  					ok = true
   390  				}
   391  			case bson.TypeDouble:
   392  				if elem.Value().Double() == 1 {
   393  					ok = true
   394  				}
   395  			}
   396  		case "errmsg":
   397  			if str, okay := elem.Value().StringValueOK(); okay {
   398  				errmsg = str
   399  			}
   400  		case "codeName":
   401  			if str, okay := elem.Value().StringValueOK(); okay {
   402  				codeName = str
   403  			}
   404  		case "code":
   405  			if c, okay := elem.Value().Int32OK(); okay {
   406  				code = c
   407  			}
   408  		case "errorLabels":
   409  			if arr, okay := elem.Value().ArrayOK(); okay {
   410  				vals, err := arr.Values()
   411  				if err != nil {
   412  					continue
   413  				}
   414  				for _, val := range vals {
   415  					if str, ok := val.StringValueOK(); ok {
   416  						labels = append(labels, str)
   417  					}
   418  				}
   419  
   420  			}
   421  		case "writeErrors":
   422  			arr, exists := elem.Value().ArrayOK()
   423  			if !exists {
   424  				break
   425  			}
   426  			vals, err := arr.Values()
   427  			if err != nil {
   428  				continue
   429  			}
   430  			for _, val := range vals {
   431  				var we WriteError
   432  				doc, exists := val.DocumentOK()
   433  				if !exists {
   434  					continue
   435  				}
   436  				if index, exists := doc.Lookup("index").AsInt64OK(); exists {
   437  					we.Index = index
   438  				}
   439  				if code, exists := doc.Lookup("code").AsInt64OK(); exists {
   440  					we.Code = code
   441  				}
   442  				if msg, exists := doc.Lookup("errmsg").StringValueOK(); exists {
   443  					we.Message = msg
   444  				}
   445  				if info, exists := doc.Lookup("errInfo").DocumentOK(); exists {
   446  					we.Details = make([]byte, len(info))
   447  					copy(we.Details, info)
   448  				}
   449  				we.Raw = doc
   450  				wcError.WriteErrors = append(wcError.WriteErrors, we)
   451  			}
   452  		case "writeConcernError":
   453  			doc, exists := elem.Value().DocumentOK()
   454  			if !exists {
   455  				break
   456  			}
   457  			wcError.WriteConcernError = new(WriteConcernError)
   458  			wcError.WriteConcernError.Raw = doc
   459  			if code, exists := doc.Lookup("code").AsInt64OK(); exists {
   460  				wcError.WriteConcernError.Code = code
   461  			}
   462  			if name, exists := doc.Lookup("codeName").StringValueOK(); exists {
   463  				wcError.WriteConcernError.Name = name
   464  			}
   465  			if msg, exists := doc.Lookup("errmsg").StringValueOK(); exists {
   466  				wcError.WriteConcernError.Message = msg
   467  			}
   468  			if info, exists := doc.Lookup("errInfo").DocumentOK(); exists {
   469  				wcError.WriteConcernError.Details = make([]byte, len(info))
   470  				copy(wcError.WriteConcernError.Details, info)
   471  			}
   472  			if errLabels, exists := doc.Lookup("errorLabels").ArrayOK(); exists {
   473  				vals, err := errLabels.Values()
   474  				if err != nil {
   475  					continue
   476  				}
   477  				for _, val := range vals {
   478  					if str, ok := val.StringValueOK(); ok {
   479  						labels = append(labels, str)
   480  					}
   481  				}
   482  			}
   483  		case "topologyVersion":
   484  			doc, ok := elem.Value().DocumentOK()
   485  			if !ok {
   486  				break
   487  			}
   488  			version, err := description.NewTopologyVersion(bson.Raw(doc))
   489  			if err == nil {
   490  				tv = version
   491  			}
   492  		}
   493  	}
   494  
   495  	if !ok {
   496  		if errmsg == "" {
   497  			errmsg = "command failed"
   498  		}
   499  
   500  		return Error{
   501  			Code:            code,
   502  			Message:         errmsg,
   503  			Name:            codeName,
   504  			Labels:          labels,
   505  			TopologyVersion: tv,
   506  			Raw:             doc,
   507  		}
   508  	}
   509  
   510  	if len(wcError.WriteErrors) > 0 || wcError.WriteConcernError != nil {
   511  		wcError.Labels = labels
   512  		if wcError.WriteConcernError != nil {
   513  			wcError.WriteConcernError.TopologyVersion = tv
   514  		}
   515  		wcError.Raw = doc
   516  		return wcError
   517  	}
   518  
   519  	return nil
   520  }