pq: errors relatively painful to use
Unless I am missing something (and feel free to correct me) in order to determine what kind of error I’ve encountered I have to do something like this:
if err, ok := err.(*pq.Error); ok {
switch err.Code.Name() {
case "unique_violation":
// ...
case "deadlock_detected":
// ....
default:
// ....
}
}
Be much nicer if instead each error code had its own error type:
switch x := err.(type) {
case nil:
// ignore
case pq.ConnectionFailure:
// do something special.
case pq.Deadlock:
// do something special.
case pq.UniqueViolation:
// do something special.
default:
// do something generic
}
example implementation: https://play.golang.org/p/AtzpqbMOp7
package main
import "fmt"
import "errors"
type PQError interface {
error
PQErrorDetails() Details
}
// Implements PQError interface
type Details struct {
Severity string
Code string
}
func (t Details) Error() string {
return t.Severity + ": " + t.Code
}
func (t Details) PQErrorDetails() Details {
return t
}
// internal function to lib/pq converts codes to their respective types.
func classify(d Details) PQError {
switch d.Code {
case "successful_completion":
return nil
case "unique_violation":
return UniquenessViolation{d}
case "deadlock_detected":
return Deadlock{d}
default:
return d
}
}
type UniquenessViolation struct { Details }
type Deadlock struct { Details }
func PrintError(err error) {
switch err := err.(type) {
case nil:
fmt.Println("No Error Hurrah")
case UniquenessViolation:
fmt.Println("Uniqueness Error Severity:", err.Severity, "Code:", err.Code)
case Deadlock:
fmt.Println("Deadlock Error")
case PQError:
fmt.Println("Some other pq error")
default:
fmt.Println("Wowa wtf", err.Error())
}
}
func HandlePQError(err PQError) PQError {
fmt.Println("Handling PQ Error")
return err
}
func HandleGenericError(err error) error {
if err != nil {
fmt.Println("Handling generic error")
} else {
fmt.Println("No Error Hurrah!")
}
return err
}
func main() {
successful_completion := classify(Details{Severity: "error", Code: "successful_completion"})
unique_violation := classify(Details{Severity: "error", Code: "unique_violation"})
deadlock := classify(Details{Severity: "error", Code: "deadlock_detected"})
generic_pq := classify(Details{Severity: "warn", Code: "ghi"})
PrintError(successful_completion)
PrintError(unique_violation)
PrintError(deadlock)
PrintError(generic_pq)
PrintError(errors.New("Something Really Unexpected Happened..."))
HandleGenericError(HandlePQError(deadlock))
HandleGenericError(successful_completion)
}
// Output
// No Error Hurrah
// Uniqueness Error Severity: error Code: unique_violation
// Deadlock Error
// Some other pq error
// Wowa wtf Something Really Unexpected Happened...
// Handling PQ Error
// Handling generic error
// No Error Hurrah!
If we wanted to get really clever we could use stringer to do the mapping from code to name and define all the codes as constants: https://godoc.org/golang.org/x/tools/cmd/stringer
About this issue
- Original URL
- State: closed
- Created 9 years ago
- Reactions: 1
- Comments: 16 (7 by maintainers)
Anyways I was just trying to offer up a better solution than what was there. If project isn’t interested its not interested ./shrug.
the point is if I as a developer using lib/pq have to do this every time I get an error from the driver:
is very annoying / painful compared to
constants wouldn’t solve this problem. I’d still need to do the switch within a switch