-
Notifications
You must be signed in to change notification settings - Fork 10
/
errors.go
204 lines (175 loc) · 5.65 KB
/
errors.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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
package pgsrv
import (
"fmt"
)
// fatalSeverity error terminates session
const fatalSeverity = "FATAL"
// Err is a postgres-compatible error object. It's not required to be used, as
// any other normal error object would be converted to a generic internal error,
// but it provides the API to generate user-friendly error messages. Note that
// all of the construction functions (prefixed with With*) are updating the same
// error, and does not create a new one. The same error is returned for
// chaining. See: https://www.postgresql.org/docs/9.3/static/protocol-error-fields.html
//
// Postgres has hundreds of different error codes, broken into categories. Use
// the constructors below (Invalid, Unsupported, etc.) to create errors with
// preset error codes. If you can't find the one you need, consider adding it
// here as a generic constructor. Otherwise, you can implement an object that
// adheres to this interface:
//
// interface {
// error
// Code() string
// }
//
// For the full list of error codes, see: https://www.postgresql.org/docs/10/static/errcodes-appendix.html
type Err error
type err struct {
S string // Severity
C string // Code
M string // Message
D string // Detail
H string // Hint
P int // Position
}
func (e *err) Severity() string { return e.S }
func (e *err) Code() string { return e.C }
func (e *err) Error() string { return e.M }
func (e *err) Detail() string { return e.D }
func (e *err) Hint() string { return e.H }
func (e *err) Position() int { return e.P }
// WithSeverity decorates an error object to also include an optional severity
func WithSeverity(err error, severity string) Err {
if err == nil {
return nil
}
e := fromErr(err)
e.S = severity
return e
}
// WithDetail decorates an error object to also include an optional secondary
// error message carrying more detail about the problem. Might run to multiple
// lines
func WithDetail(err error, detail string, args ...interface{}) Err {
if err == nil {
return nil
}
e := fromErr(err)
e.D = fmt.Sprintf(detail, args...)
return e
}
// WithHint decorates an error object to also include an optional suggestion what
// to do about the problem. This is intended to differ from Detail in that it
// offers advice (potentially inappropriate) rather than hard facts. Might run
// to multiple lines
func WithHint(err error, hint string, args ...interface{}) Err {
if err == nil {
return nil
}
e := fromErr(err)
e.H = fmt.Sprintf(hint, args...)
return e
}
// WithPosition decorates an error object to also include the cursor position
// (location) for the error in the original query string. Positions are measured
// in characters not bytes. This is useful to provide the client a specific
// marker of where the error occurred in his SQL
func WithPosition(err error, position int) Err {
if err == nil {
return nil
}
e := fromErr(err)
// keep the bottom-most position, which is the origin of the error,
// by prevent top-level errors to override actual origin position
if e.P < 0 {
e.P = position
}
return e
}
// Unrecognized indicates that a certain entity (function, column, etc.) is not
// registered or available for use.
func Unrecognized(msg string, args ...interface{}) Err {
msg = fmt.Sprintf("unrecognized "+msg, args...)
return &err{M: msg, C: "42000", P: -1}
}
// Invalid indicates that the user request is invalid or otherwise incorrect.
// It's very much similar to a syntax error, except that the invalidity is
// logical within the request rather than syntactic. For example, using a non-
// boolean expression in WHERE, or when a requested data type, table, or
// function is undefined.
func Invalid(msg string, args ...interface{}) Err {
msg = fmt.Sprintf("invalid "+msg, args...)
return &err{M: msg, C: "42000", P: -1}
}
// Disallowed indicates a permissions, authorization or permanently disallowed
// operation - access to table data, alerting users, etc.
func Disallowed(msg string, args ...interface{}) Err {
msg = fmt.Sprintf("disallowed "+msg, args...)
return &err{M: msg, C: "42000", P: -1}
}
// Unsupported indicates that a certain feature is not supported. Unlike
// Undefined - this error is not for cases where a user-space entity is not
// recognized but when the recognized entity cannot perform some of its
// functionality
func Unsupported(msg string, args ...interface{}) Err {
msg = fmt.Sprintf("unsupported "+msg, args...)
return &err{M: msg, C: "0A000", P: -1}
}
// InvalidSQLStatementName indicates that a referred statement name is
// unknown/missing to the server.
func InvalidSQLStatementName(stmtName string) Err {
msg := fmt.Sprintf("prepared statement \"%s\" does not exist", stmtName)
return &err{M: msg, C: "26000", P: -1}
}
// ProtocolViolation indicates that a provided typed message has an invalid value
func ProtocolViolation(msg string) Err {
return &err{M: msg, C: "08P01", P: -1}
}
// SyntaxError indicates that sent command is invalid
func SyntaxError(msg string, args ...interface{}) Err {
msg = fmt.Sprintf(msg, args...)
return &err{M: msg, C: "42601", P: -1, S: "ERROR"}
}
func fromErr(e error) *err {
err1, ok := e.(*err)
if ok {
return err1
}
severitier, ok := e.(interface {
Severity() string
})
s := ""
if ok {
s = severitier.Severity()
}
coder, ok := e.(interface {
Code() string
})
c := ""
if ok {
c = coder.Code()
}
m := e.Error()
detailer, ok := e.(interface {
Detail() string
})
d := ""
if ok {
d = detailer.Detail()
}
hinter, ok := e.(interface {
Hint() string
})
h := ""
if ok {
h = hinter.Hint()
}
positioner, ok := e.(interface {
Position() int
})
p := -1
if ok {
p = positioner.Position()
}
return &err{S: s, C: c, M: m, D: d, H: h, P: p}
}