github.com/cockroachdb/cockroachdb-parser@v0.23.3-0.20240213214944-911057d40c9a/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/cockroachdb-parser/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 pgcode.Code) error { 25 if err == nil { 26 return nil 27 } 28 return &withCandidateCode{cause: err, code: code.String()} 29 } 30 31 // HasCandidateCode returns tue iff the error or one of its causes 32 // has a candidate pg error code. 33 func HasCandidateCode(err error) bool { 34 return errors.HasType(err, (*withCandidateCode)(nil)) 35 } 36 37 // GetPGCodeInternal retrieves a code for the error. It operates by 38 // combining the inner (cause) code and the code at the current level, 39 // at each level of cause. 40 // 41 // - at each level: 42 // 43 // - if there is a candidate code at that level, that is used; 44 // 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 // 57 // - if the outer code has the special XX prefix, that is kept. 58 // (The "XX" prefix signals importance in the pg code hierarchy.) 59 // 60 // - if the inner code is not uncategorized, it is retained. 61 // 62 // - otherwise the outer code is retained. 63 // 64 // This function should not be used directly. It is only exported 65 // for use in testing code. Use GetPGCode() instead. 66 func GetPGCodeInternal( 67 err error, computeDefaultCode func(err error) (code pgcode.Code), 68 ) (code pgcode.Code) { 69 code = pgcode.Uncategorized 70 if c, ok := err.(*withCandidateCode); ok { 71 code = pgcode.MakeCode(c.code) 72 } else if newCode := computeDefaultCode(err); newCode.String() != "" { 73 code = newCode 74 } 75 76 if c := errors.UnwrapOnce(err); c != nil { 77 innerCode := GetPGCodeInternal(c, computeDefaultCode) 78 code = combineCodes(innerCode, code) 79 } 80 81 return code 82 } 83 84 // ComputeDefaultCode looks at the current error object 85 // (not its causes) and returns: 86 // - the existing code for Error instances 87 // - SerializationFailure for roachpb retry errors that can be reported to clients 88 // - StatementCompletionUnknown for ambiguous commit errors 89 // - InternalError for assertion failures 90 // - FeatureNotSupportedError for unimplemented errors. 91 // 92 // It is not meant to be used directly - it is only exported 93 // for use by test code. Use GetPGCode() instead. 94 func ComputeDefaultCode(err error) pgcode.Code { 95 switch e := err.(type) { 96 // If there was already a pgcode in the cause, use that. 97 case *Error: 98 return pgcode.MakeCode(e.Code) 99 // Special roachpb errors get a special code. 100 case ClientVisibleRetryError: 101 return pgcode.SerializationFailure 102 case ClientVisibleAmbiguousError: 103 return pgcode.StatementCompletionUnknown 104 } 105 106 if errors.IsAssertionFailure(err) { 107 return pgcode.Internal 108 } 109 if errors.IsUnimplementedError(err) { 110 return pgcode.FeatureNotSupported 111 } 112 return pgcode.Code{} 113 } 114 115 // ClientVisibleRetryError mirrors kvpb.ClientVisibleRetryError but 116 // is defined here to avoid an import cycle. 117 type ClientVisibleRetryError interface { 118 ClientVisibleRetryError() 119 } 120 121 // ClientVisibleAmbiguousError mirrors 122 // kvpb.ClientVisibleAmbiguousError but is defined here to avoid an 123 // import cycle. 124 type ClientVisibleAmbiguousError interface { 125 ClientVisibleAmbiguousError() 126 } 127 128 // combineCodes combines the inner and outer codes. 129 func combineCodes(innerCode, outerCode pgcode.Code) pgcode.Code { 130 if outerCode == pgcode.Uncategorized { 131 return innerCode 132 } 133 if strings.HasPrefix(outerCode.String(), "XX") { 134 return outerCode 135 } 136 if innerCode != pgcode.Uncategorized { 137 return innerCode 138 } 139 return outerCode 140 }