You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

116 lines
3.0 KiB

  1. /*
  2. Copyright 2017 Google LLC
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package spanner
  14. import (
  15. "context"
  16. "fmt"
  17. "google.golang.org/grpc"
  18. "google.golang.org/grpc/codes"
  19. "google.golang.org/grpc/metadata"
  20. )
  21. // Error is the structured error returned by Cloud Spanner client.
  22. type Error struct {
  23. // Code is the canonical error code for describing the nature of a
  24. // particular error.
  25. Code codes.Code
  26. // Desc explains more details of the error.
  27. Desc string
  28. // trailers are the trailers returned in the response, if any.
  29. trailers metadata.MD
  30. }
  31. // Error implements error.Error.
  32. func (e *Error) Error() string {
  33. if e == nil {
  34. return fmt.Sprintf("spanner: OK")
  35. }
  36. return fmt.Sprintf("spanner: code = %q, desc = %q", e.Code, e.Desc)
  37. }
  38. // decorate decorates an existing spanner.Error with more information.
  39. func (e *Error) decorate(info string) {
  40. e.Desc = fmt.Sprintf("%v, %v", info, e.Desc)
  41. }
  42. // spannerErrorf generates a *spanner.Error with the given error code and
  43. // description.
  44. func spannerErrorf(ec codes.Code, format string, args ...interface{}) error {
  45. return &Error{
  46. Code: ec,
  47. Desc: fmt.Sprintf(format, args...),
  48. }
  49. }
  50. // toSpannerError converts general Go error to *spanner.Error.
  51. func toSpannerError(err error) error {
  52. return toSpannerErrorWithMetadata(err, nil)
  53. }
  54. // toSpannerErrorWithMetadata converts general Go error and grpc trailers to *spanner.Error.
  55. // Note: modifies original error if trailers aren't nil
  56. func toSpannerErrorWithMetadata(err error, trailers metadata.MD) error {
  57. if err == nil {
  58. return nil
  59. }
  60. if se, ok := err.(*Error); ok {
  61. if trailers != nil {
  62. se.trailers = metadata.Join(se.trailers, trailers)
  63. }
  64. return se
  65. }
  66. switch {
  67. case err == context.DeadlineExceeded:
  68. return &Error{codes.DeadlineExceeded, err.Error(), trailers}
  69. case err == context.Canceled:
  70. return &Error{codes.Canceled, err.Error(), trailers}
  71. case grpc.Code(err) == codes.Unknown:
  72. return &Error{codes.Unknown, err.Error(), trailers}
  73. default:
  74. return &Error{grpc.Code(err), grpc.ErrorDesc(err), trailers}
  75. }
  76. }
  77. // ErrCode extracts the canonical error code from a Go error.
  78. func ErrCode(err error) codes.Code {
  79. se, ok := toSpannerError(err).(*Error)
  80. if !ok {
  81. return codes.Unknown
  82. }
  83. return se.Code
  84. }
  85. // ErrDesc extracts the Cloud Spanner error description from a Go error.
  86. func ErrDesc(err error) string {
  87. se, ok := toSpannerError(err).(*Error)
  88. if !ok {
  89. return err.Error()
  90. }
  91. return se.Desc
  92. }
  93. // errTrailers extracts the grpc trailers if present from a Go error.
  94. func errTrailers(err error) metadata.MD {
  95. se, ok := err.(*Error)
  96. if !ok {
  97. return nil
  98. }
  99. return se.trailers
  100. }