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 }