-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy patherror.go
129 lines (112 loc) · 3.17 KB
/
error.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package pgsql
import (
"errors"
"regexp"
"github.com/lib/pq"
)
func contains(xs []string, x string) bool {
for _, p := range xs {
if p == x {
return true
}
}
return false
}
type sqlState interface {
SQLState() string
}
// IsErrorCode checks is error has given code
func IsErrorCode(err error, code string) bool {
var sErr sqlState
if errors.As(err, &sErr) {
return sErr.SQLState() == code
}
return false
}
// IsErrorClass checks is error has given class
func IsErrorClass(err error, class string) bool {
var pqErr *pq.Error
if errors.As(err, &pqErr) && string(pqErr.Code.Class()) == class {
return true
}
return false
}
// IsUniqueViolation checks is error an unique_violation with given constraint,
// constraint can be empty to ignore constraint name checks
func IsUniqueViolation(err error, constraint ...string) bool {
var pqErr *pq.Error
if errors.As(err, &pqErr) && pqErr.Code == "23505" {
if len(constraint) == 0 {
return true
}
return contains(constraint, extractConstraint(pqErr))
}
return false
}
// IsInvalidTextRepresentation checks is error an invalid_text_representation
func IsInvalidTextRepresentation(err error) bool {
return IsErrorCode(err, "22P02")
}
// IsCharacterNotInRepertoire checks is error a character_not_in_repertoire
func IsCharacterNotInRepertoire(err error) bool {
return IsErrorCode(err, "22021")
}
// IsForeignKeyViolation checks is error an foreign_key_violation
func IsForeignKeyViolation(err error, constraint ...string) bool {
var pqErr *pq.Error
if errors.As(err, &pqErr) && pqErr.Code == "23503" {
if len(constraint) == 0 {
return true
}
return contains(constraint, extractConstraint(pqErr))
}
return false
}
// IsQueryCanceled checks is error an query_canceled error
// (pq: canceling statement due to user request)
func IsQueryCanceled(err error) bool {
return IsErrorCode(err, "57014")
}
// IsSerializationFailure checks is error a serialization_failure error
// (pq: could not serialize access due to read/write dependencies among transactions)
func IsSerializationFailure(err error) bool {
return IsErrorCode(err, "40001")
}
func extractConstraint(err *pq.Error) string {
if err.Constraint != "" {
return err.Constraint
}
if err.Message == "" {
return ""
}
if s := extractCRDBKey(err.Message); s != "" {
return s
}
if s := extractLastQuote(err.Message); s != "" {
return s
}
return ""
}
var reLastQuoteExtractor = regexp.MustCompile(`"([^"]*)"[^"]*$`)
// extractLastQuote extracts last string in quote
// ex. `insert or update on table "b" violates foreign key constraint "a_id_fkey"`
// will return `a_id_fkey`
func extractLastQuote(s string) string {
rs := reLastQuoteExtractor.FindStringSubmatch(s)
if len(rs) < 2 {
return ""
}
return rs[1]
}
var reCRDBKeyExtractor = regexp.MustCompile(`(\w+@\w+)[^@]*$`)
// extractCRDBKey extracts key from crdb
// until (https://github.com/cockroachdb/cockroach/issues/36494) resolved
// ex. `foreign key violation: value ['b'] not found in a@primary [id] (txn=e3f9af56-5f73-4899-975c-4bb1de800402)`
// will return `a@primary`
func extractCRDBKey(s string) string {
rs := reCRDBKeyExtractor.FindStringSubmatch(s)
if len(rs) < 2 {
return ""
}
return rs[1]
}