github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/pgwire/pgerror/pgcode.go (about) 1 // Copyright 2019 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package pgerror 12 13 import ( 14 "strings" 15 16 "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode" 17 "github.com/cockroachdb/errors" 18 ) 19 20 // WithCandidateCode decorates the error with a candidate postgres 21 // error code. It is called "candidate" because the code is only used 22 // by GetPGCode() below conditionally. 23 // The code is considered PII-free and is thus reportable. 24 func WithCandidateCode(err error, code string) error { 25 if err == nil { 26 return nil 27 } 28 29 return &withCandidateCode{cause: err, code: code} 30 } 31 32 // HasCandidateCode returns tue iff the error or one of its causes 33 // has a candidate pg error code. 34 func HasCandidateCode(err error) bool { 35 return errors.HasType(err, (*withCandidateCode)(nil)) 36 } 37 38 // GetPGCodeInternal retrieves a code for the error. It operates by 39 // combining the inner (cause) code and the code at the current level, 40 // at each level of cause. 41 // 42 // - at each level: 43 // 44 // - if there is a candidate code at that level, that is used; 45 // - otherwise, it calls computeDefaultCode(). 46 // if the function returns an empty string, 47 // UncategorizedError is used. 48 // An example implementation for computeDefaultCode is provided below. 49 // 50 // - after that, it combines the code computed already for the cause 51 // (inner) and the new code just computed at the current level (outer) 52 // as follows: 53 // 54 // - if the outer code is uncategorized, the inner code is kept no 55 // matter what. 56 // - if the outer code has the special XX prefix, that is kept. 57 // (The "XX" prefix signals importance in the pg code hierarchy.) 58 // - if the inner code is not uncategorized, it is retained. 59 // - otherwise the outer code is retained. 60 // 61 // This function should not be used directly. It is only exported 62 // for use in testing code. Use GetPGCode() instead. 63 func GetPGCodeInternal(err error, computeDefaultCode func(err error) (code string)) (code string) { 64 code = pgcode.Uncategorized 65 if c, ok := err.(*withCandidateCode); ok { 66 code = c.code 67 } else if newCode := computeDefaultCode(err); newCode != "" { 68 code = newCode 69 } 70 71 if c := errors.UnwrapOnce(err); c != nil { 72 innerCode := GetPGCodeInternal(c, computeDefaultCode) 73 code = combineCodes(innerCode, code) 74 } 75 76 return code 77 } 78 79 // ComputeDefaultCode looks at the current error object 80 // (not its causes) and returns: 81 // - the existing code for Error instances 82 // - SerializationFailure for roachpb retry errors that can be reported to clients 83 // - StatementCompletionUnknown for ambiguous commit errors 84 // - InternalError for assertion failures 85 // - FeatureNotSupportedError for unimplemented errors. 86 // 87 // It is not meant to be used directly - it is only exported 88 // for use by test code. Use GetPGCode() instead. 89 func ComputeDefaultCode(err error) string { 90 switch e := err.(type) { 91 // If there was already a pgcode in the cause, use that. 92 case *Error: 93 return e.Code 94 // Special roachpb errors get a special code. 95 case ClientVisibleRetryError: 96 return pgcode.SerializationFailure 97 case ClientVisibleAmbiguousError: 98 return pgcode.StatementCompletionUnknown 99 } 100 101 if errors.IsAssertionFailure(err) { 102 return pgcode.Internal 103 } 104 if errors.IsUnimplementedError(err) { 105 return pgcode.FeatureNotSupported 106 } 107 return "" 108 } 109 110 // ClientVisibleRetryError mirrors roachpb.ClientVisibleRetryError but 111 // is defined here to avoid an import cycle. 112 type ClientVisibleRetryError interface { 113 ClientVisibleRetryError() 114 } 115 116 // ClientVisibleAmbiguousError mirrors 117 // roachpb.ClientVisibleAmbiguousError but is defined here to avoid an 118 // import cycle. 119 type ClientVisibleAmbiguousError interface { 120 ClientVisibleAmbiguousError() 121 } 122 123 // combineCodes combines the inner and outer codes. 124 func combineCodes(innerCode, outerCode string) string { 125 if outerCode == pgcode.Uncategorized { 126 return innerCode 127 } 128 if strings.HasPrefix(outerCode, "XX") { 129 return outerCode 130 } 131 if innerCode != pgcode.Uncategorized { 132 return innerCode 133 } 134 return outerCode 135 }